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

@builtbyted/homeframe

v0.1.15

Published

A React framework for stable iOS Home Screen and desktop progressive web apps.

Downloads

2,309

Readme

@builtbyted/homeframe

Homeframe is a React framework for apps that live on the iOS Home Screen and as installed desktop PWAs. It owns the browser behavior that ordinary app code should not have to rebuild: viewport and safe-area geometry, keyboard docking, the app shell, startup presentation, routing/history, generated install assets, the service worker, atomic updates, offline navigation, installation capability, notifications, lifecycle restore, and diagnostics.

Create an app

npx scaffold-homeframe-app my-app
cd my-app
npm run dev

The scaffold includes strict TypeScript, a working shell/router composition, generated PWA assets, AGENTS.md, and docs/HOMEFRAME_RUNBOOK.md so human and AI coders know the framework's ownership boundaries.

The generated contract keeps one persistent shell/header with one active primary scroller around the route outlet, preserves an opaque iOS safe-area header surface, forbids app-level viewport/splash workarounds, and requires native iPhone keyboard open/close plus open-keyboard scrolling checks before release.

Install manually

Route loaders run again on fresh navigations. A prefetched result can serve one navigation within 30 seconds; Back/Forward can reuse data for up to 60 seconds. The router retains at most 100 data entries and six DOM previews. Older managed history entries remain navigable and use an app-canvas preview until rendered.

After a mutation, call await router.revalidate() to refresh the current route while preserving its history entry and scroll. Call router.invalidate() before changing accounts, then navigate to the next screen; router.invalidate(url) clears a specific URL. Invalidation cancels pending work and clears retained data and previews; it does not itself navigate or replace the currently rendered UI. useHomeframeLogout() also invalidates active Homeframe routers.

npm install @builtbyted/homeframe react react-dom
npm install -D vite @vitejs/plugin-react typescript

Use the primary package for React shell and router APIs:

import {
  AppScrollView,
  AppShell,
  AppViewport,
  HomeframeProvider,
  HomeframeRouterProvider,
  RouterOutlet,
  createHomeframeRouter,
  useRouteScrollRestoration,
} from '@builtbyted/homeframe';
import '@builtbyted/homeframe/styles.css';

Configure generated PWA behavior through the Vite subpath:

import { defineHomeframe } from '@builtbyted/homeframe/vite';

export default defineHomeframe({
  app: {
    id: '/',
    name: 'My App',
    shortName: 'My App',
    startUrl: '/',
    scope: '/',
    display: 'standalone',
    colorScheme: 'system',
    themeColor: '#dbeafe',
    themeColorDark: '#0f172a',
    backgroundColor: '#dbeafe',
    backgroundColorDark: '#0f172a',
    icon: './brand/icon.svg',
  },
  viewport: {
    // Disable incidental selection everywhere, allow it only on desktop, or
    // retain normal browser selection everywhere.
    selection: 'allow-desktop',
    snapshot: 'brand',
    bottomDock: 'avoid',
  },
  router: { historyMode: 'auto' },
  serviceWorker: {
    update: { mode: 'automatic', reload: 'safe-point' },
  },
});
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { homeframe } from '@builtbyted/homeframe/vite';
import homeframeConfig from './homeframe.config.js';

export default defineConfig({
  plugins: [homeframe(homeframeConfig), react()],
});

Keep index.html intentionally small. Homeframe generates the viewport, theme-color, install, Apple, startup, manifest, and service-worker metadata.

Dock placement and keyboard behavior compose independently. For a bottom search field or composer that overlays content while following the software keyboard:

<ViewportDock placement="overlay" keyboard="avoid">
  <SearchComposer />
</ViewportDock>

Homeframe owns its safe areas, measurement, hit testing, and keyboard translation; no app-level viewport positioning rule is required.

For a player or tool row attached below the header, or a search row stacked above bottom navigation while content scrolls behind it, use the measured shell slots:

<AppShell
  header={<Header />}
  headerAttachment={<VideoPlayer />}
  bottom={<BottomNavigation />}
  bottomAttachment={<SearchComposer />}
  bottomAttachmentKeyboard="avoid"
>
  <AppScrollView>{children}</AppScrollView>
</AppShell>

If the keyboard should cover persistent bottom navigation while only the attachment follows it, set bottomKeyboard="manual" and keep bottomAttachmentKeyboard="avoid". Homeframe removes the covered dock row from the attachment anchor while the attachment owns keyboard focus.

ViewportAttachment is also exported for direct composition. Homeframe owns its position and size but intentionally supplies no colors, fonts, borders, or shadows. homeframe doctor --strict reports app-authored fixed/sticky regions as HF_UNTRACKED_VIEWPORT_UI; the ESLint plugin catches inline versions while editing when enabled in the application's ESLint configuration.

Package entry points

  • @builtbyted/homeframe — React shell and router APIs.
  • @builtbyted/homeframe/styles.css — required framework styles.
  • @builtbyted/homeframe/react — React APIs without router re-exports.
  • @builtbyted/homeframe/router — router APIs.
  • @builtbyted/homeframe/vite — typed configuration and Vite plugin.
  • @builtbyted/homeframe/runtime — advanced non-React runtime APIs.
  • @builtbyted/homeframe/sw — advanced worker/client APIs.
  • @builtbyted/homeframe/eslint-plugin — framework boundary lint rules.
  • homeframe — bundled CLI binary with init, migrate, upgrade, and doctor.

Framework boundaries

  • The document never scrolls; content scrolls in AppScrollView.
  • Use one HomeframeProvider, one router provider, and one top-level AppViewport.
  • Put persistent chrome in AppShell; put bottom controls in its keyboard-aware bottom slot or a Homeframe dock.
  • Do not register another service worker or hand-author generated PWA metadata.
  • Do not use raw viewport measurements or device-specific safe-area constants.
  • Use Homeframe editable controls and keep editable text at least 16 CSS px.
  • Preserve app identity and worker scope after users install the app.

Run the release-oriented checks with:

npm run build
npx homeframe doctor --root . --dist dist --strict

Documentation

MIT © BuiltByTed