npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

frameset

v0.1.1

Published

A lightweight viewer for iterating on HTML frames alongside a real codebase. A frame is an HTML fragment that imports real components and styles. Frameset discovers those files, wraps them into full documents at serve time, and shows them in a live viewer

Readme

Frameset

A lightweight viewer for iterating on HTML frames alongside a real codebase. A frame is an HTML fragment that imports real components and styles. Frameset discovers those files, wraps them into full documents at serve time, and shows them in a live viewer with hot reload.

Storybook targets isolated component stories. Figma targets visual design. Frameset targets the space between: composing real components into real screens quickly, especially in agent-driven workflows where editing plain HTML is the simplest thing that can work.

Quick start

frameset init
frameset

Or point at an existing project:

frameset --path my-project

This starts a dev server on http://127.0.0.1:4400/, opens the viewer in a browser, and watches for changes.

To work on the viewer UI itself, run:

npm run dev

This runs Frameset against the local example/ project, which is the default sandbox for iterating on the viewer UI and example frames together.

Project setup

A Frameset project needs a directory of *.html frame files (default: frames/). frameset.config.js (or .ts) is optional and only needed when you want to customize settings like dir or globals.

my-project/
  frameset.config.js
  frames/
    dashboard.html
    login.html
  components/
    nav-bar.js
    metric-card.js

Run frameset init to scaffold a starter config and frames directory. Or create just the frames directory by hand:

Config

// frameset.config.js
export default {
  dir: "frames",
  globals: {
    theme: {
      default: "light",
      values: ["light", "dark"],
    },
  },
  applyGlobals({ globals, document }) {
    document.documentElement.dataset.theme = globals.theme;
  },
};
  • dir — directory to scan for frame HTML files, relative to the project root. Defaults to "frames".
  • globals — defines dropdowns that appear in the viewer toolbar. Each key becomes a dropdown. default is optional; if omitted, the first value is used.
  • applyGlobals({ globals, frame, document, window }) — called inside each frame when a global value changes. Receives a single context object. document and window refer to the frame, not the viewer.

Config changes take effect without restarting the server.

Frames

A frame is a body-only HTML fragment — no <!DOCTYPE>, <html>, <head>, or <body> tags. Frameset wraps it into a full document automatically.

<!-- frames/login.html -->
<title>Login card</title>
<script type="module" src="../components/login-panel.js"></script>

<style>
  body { background: #f8fafc; }
  :root[data-theme="dark"] body { background: #020617; }
</style>

<div class="page">
  <login-panel title="Welcome back" primary-label="Continue"></login-panel>
</div>
  • An optional <title> tag sets the display name in the sidebar. If omitted, the filename is used.
  • <script> and <style> tags work normally. Relative paths resolve from the frame file's location.
  • Frames are served through Vite, so any transforms from your vite.config.js apply automatically.

Frames are discovered from the top level of the frame directory. Nested subdirectories are ignored for now.

Commands

frameset — start dev server

frameset [options]

| Option | Description | Default | |---|---|---| | --path <path> | Project root directory | Current working directory | | --dir <name> | Override the frame directory | "frames" | | --port <number> | Dev server port | 4400 |

frameset init — scaffold a new project

frameset init [options]

| Option | Description | Default | |---|---|---| | --dir <name> | Frame directory name | "frames" |

Creates frameset.config.js and the frames directory.

Viewer

The viewer shows a sidebar listing all discovered frames and an iframe preview of the selected frame. If globals are configured, a toolbar with dropdown controls appears below the preview.

The selected frame is stored in the URL hash, so you can bookmark or share links like http://127.0.0.1:4400/#dashboard.

Direct frame routes

Each frame is also available at its own URL:

http://127.0.0.1:4400/frame/dashboard
http://127.0.0.1:4400/frame/login

These are full HTML pages, useful for testing with Playwright, visual diffing, or accessibility checks — no special story format needed.

Vite integration

Frameset creates a Vite dev server rooted at your project. If you have a vite.config.js, Frameset inherits your plugins, aliases, PostCSS config, and framework transforms. Frameset only overrides what it needs to control (server port, host, and its own routes).

Viewer-only dependencies belong to Frameset itself. If the viewer imports a linked local package, Frameset needs an explicit Vite alias for it in src/server.ts so the viewer still resolves correctly when the dev server root points at another project.

Testing

Since frames are plain URLs, standard browser testing tools work directly:

// Playwright example
await page.goto("http://127.0.0.1:4400/frame/dashboard");
await expect(page.locator("metric-card")).toBeVisible();