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

@techrox/page-studio

v1.0.0

Published

Drag-and-drop visual page builder for React, built on Puck. Adapter-driven persistence, brand theming, sidebar block library, replaceable top bar.

Readme

@techrox/page-studio

The editor shell for Page Studio — a drop-in <PageStudio /> component that gives you a visual page builder backed by Puck.

pnpm add @techrox/page-studio @techrox/page-studio-blocks
pnpm add @puckeditor/core antd @ant-design/icons   # peers

Usage

'use client';

import { PageStudio } from '@techrox/page-studio';
import '@techrox/page-studio/styles.css';
import '@techrox/page-studio-blocks/styles.css';

export default function BuilderRoute({ pageKey, initialData }) {
  return (
    <PageStudio
      pageKey={pageKey}
      initialData={initialData}
      pageTitle="About us"
      adapter={{
        savePage: async (key, data) => api.savePage(key, data),
        loadPage: async (key) => api.loadPage(key),       // optional if initialData is provided
        onCreatePage: () => router.push('/admin/pages/new'),  // optional — shows "New page" button
      }}
      branding={{
        name: 'Acme CMS',
        logo: <SvgLogo />,
        primaryColor: '#0F766E',
      }}
      studio={{ Link, services, site, submitLead, track }}
      account={{ name: 'Jane', email: '[email protected]' }}
      onSignOut={() => signOut()}
      homeHref="/admin/pages"
      livePath="/about"
    />
  );
}

Props

| Prop | Type | Notes | |---|---|---| | pageKey | string | Required. Logical key for the page; passed to adapter functions. | | initialData | PuckData | Optional. If supplied, loadPage is skipped — best for SSR. | | pageTitle | string | Human-readable label shown in the top bar crumb. | | adapter | { loadPage?, savePage?, onCreatePage? } | Async functions the editor calls. savePage is required for publish. If onCreatePage is set, a "New page" button appears in the top bar. | | studio | StudioValue | Forwarded to <PageStudioProvider> so blocks see Link, services, site, submitLead, subscribeNewsletter, track. See @techrox/page-studio-blocks. | | branding | { name?, logo?, primaryColor?, accentColor?, inkColor? } | Drives the top-bar identity + CSS variables (--psd-primary, --psd-accent, --psd-ink). | | header | ReactNode \| (props) => ReactNode | Fully replace the default top bar. If a function, receives { pageKey, pageTitle, account, livePath, savedAt, pending, onPublish, onSignOut, onCreatePage, branding, extraActions, LinkComponent }. | | headerActions | ReactNode | Extra buttons appended to the default top bar (between View live + Publish). | | account | { name, email } | If set, an avatar/dropdown appears at the top right. | | onSignOut | () => void | Optional sign-out handler for the account dropdown. | | homeHref | string | If set, the brand mark + "Admin home" menu entry link here. | | livePath | string | If set, a "View live" button opens this path in a new tab. | | config | PuckConfig | Override the Puck config (use createPuckConfig from blocks). | | overrides | PuckOverrides | Override Puck overrides (defaults to the block-card drawer item). | | sidebarLabels | { blocks?, layers? } | Labels for the injected sidebar tabs. Default: "Blocks" / "Layers". | | LinkComponent | Component | Component used for top-bar links (defaults to <a>). Pass next/link or React Router's Link for client-side navigation. |

What the editor renders

The default top bar shows:

  • Brand mark + name on the left (links to homeHref if set)
  • Page title crumb in the centre
  • Right side: optional headerActions, optional "New page" (if onCreatePage set), optional "View live" (if livePath set), the Publish button, optional account avatar

The sidebar is Puck's, enhanced with:

  • A Blocks / Layers tab bar at the top
  • A count badge on each component category header

These enhancements live in BuilderEnhancements.jsx and are pure post-mount DOM mutations against Puck's emitted classnames — kept resilient to minor Puck version changes by tagging sections via content rather than DOM order.

Adapter contract

type Adapter = {
  loadPage?:    (pageKey: string) => Promise<PuckData>;
  savePage?:    (pageKey: string, data: PuckData) => Promise<void>;
  onCreatePage?: () => void;
};
  • The editor does not know how you persist data. JWT, session cookies, signed URLs, anything — savePage is your line of integration.
  • Both loadPage and savePage should throw on failure. The editor turns errors into AntD message toasts.
  • onCreatePage is a callback, not an adapter function — typically just router.push('/new'). The button stays hidden unless the prop is set.

Customising the top bar

For small additions, use headerActions to slot extra buttons next to Publish.

For full control, pass header as a render function:

<PageStudio
  header={({ onPublish, pending, pageTitle }) => (
    <MyCustomBar title={pageTitle} onPublish={onPublish} saving={pending} />
  )}
  // ...
/>

Your function receives all the same props the default top bar uses, so you can pick and choose what to render.

Required host CSS

The editor ships:

  • @techrox/page-studio/styles.css — editor chrome (top bar, sidebar tabs, loading state)
  • @techrox/page-studio-blocks/styles.css — block-card picker UI + reveal animations

Block typography classes (.tps-h1, .tps-section, .tps-container, .tps-lede, etc.) are not in the package — they live in your host stylesheet. The blocks reference these class names but expect the host to define them. See @techrox/page-studio-blocks README for the full list.

License

MIT.