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

@pagehub/sdk

v0.1.8

Published

Embeddable visual page builder SDK — drop a full drag-and-drop page editor into any web app, or render published sites anywhere JS runs.

Readme

@pagehub/sdk

Drop a full visual page builder into any web app.

A drag-and-drop page editor SDK built on CraftJS. Includes a visual canvas, component toolbox, settings panels, responsive preview, multi-page support, AI generation, SEO controls, and static HTML export — all configured through a single config object.

Install

@pagehub/sdk ships three sub-path entry points so you only pay for what you use:

| Use case | Import | Extras to install | |---|---|---| | Editor (drag-and-drop builder) | @pagehub/sdk | the editor-extras snippet below | | React viewer (render a published site in a React app) | @pagehub/sdk/react | none (just React) | | Static HTML (render to an HTML string in Workers / Deno / Bun / Node) | @pagehub/sdk/html | none |

Viewer / static-only install

npm install @pagehub/sdk react react-dom

Editor install (adds chrome deps as optional peers)

npm install @pagehub/sdk react react-dom \
  @tiptap/core @tiptap/pm @tiptap/react @tiptap/starter-kit \
  @tiptap/extension-bold @tiptap/extension-code @tiptap/extension-color \
  @tiptap/extension-document @tiptap/extension-font-family \
  @tiptap/[email protected] @tiptap/extension-highlight \
  @tiptap/extension-image @tiptap/extension-italic @tiptap/extension-link \
  @tiptap/extension-placeholder @tiptap/extension-strike \
  @tiptap/extension-subscript @tiptap/extension-superscript \
  @tiptap/extension-text-align @tiptap/extension-text-style \
  @tiptap/extension-underline \
  @codemirror/autocomplete @codemirror/lang-css @codemirror/lang-html \
  @codemirror/lang-javascript @codemirror/language @codemirror/lint \
  @codemirror/view @uiw/react-codemirror @lezer/highlight \
  @tailwindcss/browser @floating-ui/react-dom @headlessui/react \
  @hello-pangea/color-picker use-eye-dropper react-window swr \
  gsap framer-motion

Component-level optional peers

Only install if your site actually uses these components:

# Map component
npm install leaflet react-leaflet

# Video component (YouTube provider)
npm install react-youtube

Developing inside the monorepo: see CONTRIBUTING.md for setup instructions.


Quick Start

React / Next.js

import { PageHubEditor } from "@pagehub/sdk";
import "@pagehub/sdk/editor.css";

export default function Builder() {
  return (
    <PageHubEditor
      callbacks={{
        onLoad: async () => {
          const res = await fetch("/api/pages/home");
          return res.ok ? res.json() : null; // null = blank canvas
        },
        onSave: async pageData => {
          await fetch("/api/pages/home", {
            method: "PUT",
            body: JSON.stringify(pageData),
          });
        },
      }}
      theme={{ primaryColor: "#2563eb" }}
    />
  );
}

SSR note: The editor uses browser APIs. In Next.js, load it with next/dynamic:

const Builder = dynamic(() => import("./Builder"), { ssr: false });

Vanilla JS (any framework)

<div id="editor" style="height: 100vh"></div>
<script type="module">
  import PageHub from "@pagehub/sdk";
  import "@pagehub/sdk/editor.css";

  const editor = PageHub.init({
    container: "#editor",
    callbacks: {
      onLoad: async () => {
        const res = await fetch("/api/pages/home");
        return res.ok ? res.json() : null; // null = blank canvas
      },
      onSave: async pageData => {
        await fetch("/api/pages/home", {
          method: "PUT",
          body: JSON.stringify(pageData),
        });
      },
    },
  });
</script>

Configuration

resolveConfig(config)

| Option | Type | Default | Description | | ------------ | ----------------------- | ---------- | ------------------------------------------------------- | | container | string \| HTMLElement | — | DOM selector or element to mount into (vanilla JS only) | | apiKey | string | — | PageHub Cloud API key (optional for self-hosted) | | apiBaseUrl | string | "" | API base URL (for AI, uploads, forms) | | pageId | string | — | Initial page ID to load | | readOnly | boolean | false | Start in viewer mode | | callbacks | PageHubCallbacks | required | Your integration hooks (see below) | | theme | PageHubTheme | — | Visual theming | | features | PageHubFeatures | — | Feature toggles | | ai | PageHubAIConfig | — | AI generation config | | locale | PageHubLocale | — | Localization overrides |

Callbacks

The two required callbacks are how you connect PageHub to your backend:

callbacks: {
  onLoad: (pageId?) => Promise<PageData | null>,  // fetch page — return null for blank canvas
  onSave: (pageData, meta?) => Promise<void>,      // persist page

  // Optional
  onChange: (pageData) => void,            // fires on every edit (debounced) — useful for autosave
  onPublish: (pageData) => Promise<void>,  // user clicked "Publish"
  onMediaUpload: (file) => Promise<string>,// upload a file, return the public URL
  onMediaDelete: (url) => Promise<void>,   // delete a previously uploaded file
}

The pageData object you receive on save:

{
  content: string;    // compressed editor state — store this to reload later
  html?: string;      // rendered static HTML — ready to publish
  classes?: string[];  // Tailwind classes used — for CSS purging/compilation
  title?: string;
  seo?: PageSeo;
}

Theme

theme: {
  primaryColor: "#2563eb",
  secondaryColor: "#7c3aed",
  accentColor: "#06b6d4",
  colorScheme: "system",    // "light" | "dark" | "system"
  logo: "/your-logo.svg",   // shown in editor toolbar
  cssVariables: { ... },     // custom CSS vars (no -- prefix needed)
  customCSS: "...",           // raw CSS injected into the editor
}

Adopting host design tokens (shadcn / Tailwind v4): if your app already defines a shadcn-style palette under --color-* vars, add one import to rebind PageHub's DaisyUI tokens to the host vars:

import "@pagehub/sdk/editor.css";
import "@pagehub/sdk/themes/shadcn.css";   // load AFTER editor.css

Maps --color-background → --base-100, --color-foreground → --base-content, --color-primary → --primary, --radius → --radius-box/-field/-selector, etc. Each binding falls back to the DaisyUI default if the host hasn't defined the matching shadcn var. See docs/sdk/theme.md for the full mapping table.

Features

Toggle editor capabilities on or off:

features: {
  sidebar: true,                  // component panel
  toolbar: true,                  // top toolbar
  saveButton: true,               // save/publish button in top toolbar
  aiGeneration: false,            // AI content generation (requires ai config)
  multiPage: true,                // multi-page site editing
  responsivePreview: true,        // device preview toggle
  seoPanel: true,                 // SEO settings
  importExport: true,             // Import/Export row in More menu
  settingsPanelSwitcher: true,    // "Left/Right Settings Panel" row in More menu
  darkModeSwitcher: true,         // "Switch to Dark/Light Theme" row in More menu
  customCSS: false,               // CSS editor panel
  restrictedComponents: [],       // component names to hide from toolbox
}

AI

ai: {
  enabled: true,             // requires a PageHub account
}

AI features (content generation, assistant) are powered by PageHub's API. Users can configure their own AI provider keys in their PageHub account settings.


Instance API

PageHub.init() returns an instance with these methods:

const editor = PageHub.init({ ... });

// Save & load
editor.save({ isDraft: true });
editor.load("page-id");
editor.getPageData();            // { content, html, classes, ... }

// HTML export
editor.getHTML();                // rendered HTML string
editor.exportHTML({              // full static HTML + metadata
  document: true,               // wrap in <html><head>...</head><body>
  title: "My Page",
  includeThemeVars: true,
});

// JSON import/export
editor.exportJSON();             // raw CraftJS JSON string
editor.importJSON(jsonString);   // load from JSON

// Runtime updates
editor.setReadOnly(true);        // toggle viewer mode
editor.setTheme({ primaryColor: "#e11d48" });
editor.setFeatures({ sidebar: false });

// Events
const off = editor.on("save", (data) => console.log("Saved!", data));
off(); // unsubscribe

// Cleanup
editor.destroy();

Events

| Event | Payload | When | | ------------------- | ---------------------- | -------------------------------- | | ready | — | Editor mounted and ready | | save | PageData | User or programmatic save | | load | PageData | Page loaded | | change | PageData | Editor state changed (debounced) | | publish | PageData | User published | | error | Error | Something went wrong | | modeChange | "editor" \| "viewer" | Read-only toggled | | componentSelect | node info | User selected a component | | componentDeselect | — | Selection cleared |


Three runtimes — import only what you need

The SDK ships as three independent entry points. Pages built in the full editor render identically through all three:

| Entry | Use for | Ships | Doesn't ship | | ------------------------------ | ------------------------------------------------ | -------------------------------------------------------------------- | --------------------------------------------------------- | | @pagehub/sdk | The visual editor | Full editor chrome, toolbox, inspector, TipTap, CodeMirror, GSAP | — | | @pagehub/sdk/viewer | Live published pages (React) | React render runtime, theme + animation presets, action handlers | Editor chrome, TipTap, CodeMirror, GSAP editor harness | | @pagehub/sdk/static-renderer | SSG / Node / edge / email HTML / build scripts | Pure string-based HTML walker, theme CSS, class collector | React, ReactDOM, any browser API |

Use the editor on /edit/[id], the viewer on /preview/[id], and the static renderer in getStaticProps, an edge function, or a Node export script. Mix and match — same content payload everywhere.


Viewer — read-only React mode

Render saved pages with interactivity (forms, modals, scroll animations, conditional visibility) but without the editor:

import { PageHubViewer } from "@pagehub/sdk/viewer";

<PageHubViewer content={savedContent} resolver={resolver} />;
  • No editor chrome. Toolbox, inspector, settings panels, and the pagehub-sdk-root shell are all gone — just the page.
  • No heavy editor deps. TipTap, CodeMirror, GSAP editor harness, and the prop-system UI are tree-shaken out.
  • Tailwind injected at runtime. No stylesheet import needed; pass your own theme CSS via the host page if you want SSR-correct styling.
  • Same content payload. Whatever onSave gave you is what the viewer reads — no transform step.

Good fit for: live published sites, preview routes, in-app page rendering, A/B test variants.


Static HTML Renderer — no React, no browser

Render pages to a plain HTML string from anywhere a function can run — Node scripts, edge workers, getStaticProps, email-template pipelines, search-engine snapshots:

import { renderToHTML } from "@pagehub/sdk/static-renderer";

// From compressed content (what onSave gives you)
const { html, classes, fontUrls } = renderToHTML(savedContent);

// From raw JSON
const { html } = renderToHTML(jsonString, { compressed: false });

// Full standalone document
const { html } = renderToHTML(savedContent, {
  document: true,
  title: "My Page",
});
  • Zero React dependency. Walks the node tree and emits strings directly — runs in Cloudflare Workers, Deno, Bun, and Node without polyfills.
  • Returns everything you need to ship. html for the body, classes for Tailwind compilation/purging, fontUrls for <link> tags.
  • Identical output to the viewer. Same theme, same component registry, same action wiring — interactive scripts are inlined into the HTML where needed.
  • document: true wraps the output in a full <html><head>...</head><body> document, ready to write to disk or pipe into an email.

Good fit for: SSG (Next.js / Astro / 11ty), next export, on-publish HTML uploads to a CDN, email rendering, search-engine prerender, static site exports for hosting elsewhere.

Note on Container.overflow.* — when a container uses overflow.dragScroll, overflow.autoHide, overflow.wheelHorizontal, overflow.smoothing, or overflow.hideDelay, both the viewer and renderToHTML add overflow-x-auto to className (unless another overflow-x-* utility is already set) and inline a small script that enables pointer-drag and wheel-to-horizontal scrolling. These are CSS overflow UX options, not the GSAP scrollEffect (horizontal-scroll / scroll-timeline).


Built-in Components

The SDK ships with these drag-and-drop components out of the box:

| Component | Description | | ---------------- | ----------------------------------------- | | Container | Flex/grid layout wrapper | | Header | Page header with nav | | Footer | Page footer | | Text | Rich text with inline editing | | Image | Responsive images | | Button | CTA button with variants | | Video | Video embed (YouTube, Vimeo, self-hosted) | | Audio | Audio player | | Embed | Raw HTML / iframe embed | | Form | Form container | | FormElement | Input, select, textarea, etc. | | Background | Full-bleed background section |

Custom Components

Register your own draggable component with defineComponent. One file, one function call, no SDK fork — the same components array is read by the editor, viewer, and static renderer.

import { PageHubEditor, defineComponent } from "@pagehub/sdk";

const PricingCard = ({ title, price, period }) => (
  <div className="rounded-box border p-6">
    <h3>{title}</h3>
    <p>${price}/{period}</p>
  </div>
);

const PricingCardDef = defineComponent({
  name: "PricingCard",
  category: "Marketing",
  component: PricingCard,
  defaultProps: { title: "Pro", price: 29, period: "mo" },
  props: {
    title: { type: "text", label: "Plan Name" },
    price: { type: "number", label: "Price", min: 0 },
    period: { type: "select", label: "Period", options: [
      { label: "Month", value: "mo" },
      { label: "Year", value: "yr" },
    ]},
  },
  toHTML: ({ props }) =>
    `<div class="rounded-box border p-6"><h3>${props.title}</h3><p>$${props.price}/${props.period}</p></div>`,
});

<PageHubEditor
  components={[PricingCardDef]}
  callbacks={{ onLoad, onSave }}
/>;

Vanilla JS is the same — pass components: [PricingCardDef] into PageHub.init().

Working starters: PageHubApp/demos — runnable vanilla-html, vite-react, and nextjs integrations (the vite-react / nextjs ones register a custom defineComponent).

Full reference: docs/sdk/registration-host.md — every field on defineComponent, custom inspector UIs, toHTML rules, viewer/static parity, common gotchas.

If you're modifying the SDK itself to add a new built-in (alongside Container / Text / Button), the surface is different — 8 registrations inside packages/sdk. See docs/sdk/registration.md. Host-app custom components do not need that checklist.


CSS

Import the editor stylesheet in your app:

import "@pagehub/sdk/editor.css";

Vite bundles src/css/editor.css into dist/editor.css (same public path: @pagehub/sdk/editor.css). Source is split under src/css/editor-partials/*.css for maintainability; the entry file only wires Tailwind (@import 'tailwindcss'), @reference / @source, and an @import chain.

Scoping: Almost all editor chrome rules are prefixed with .pagehub-sdk-root so generic selectors (button, .input, #viewport, [data-enabled], scrollbars, etc.) do not hit the host page when the builder is embedded. ph-anim-* / ph-hover-* / css-* keyframes stay global so saved page content and static export still work. Mobile preview toggles mobile-preview on .pagehub-sdk-root (not <html>). Portaled listboxes set pagehub-sdk-root ph-select-content on the panel so dropdown styles apply after teleport.

Theming (variables first)

  • Prefer theme tokens from your host: load shared design CSS (for PageHub apps this is your design system CSS) so --base-100, --primary, --border, etc. match the editor chrome.
  • config.theme.cssVariables and config.theme.customCSS still apply on top for integration-specific overrides.
  • @tailwindcss/browser (used for in-canvas utilities) compiles from live class attributes; @source in editor.css scans SDK sources (../).

Editor CSS partials (concern → file)

Partials that use @apply / @utility rely on css/editor-partials/tailwind-theme-reference.css, imported immediately after tailwindcss in editor.css. @reference URLs are resolved from packages/sdk/src/css/, not from the partial file path. Add new @import lines only at the top of editor.css (PostCSS requires every @import before @source / other at-rules).

| Concern | File | | ----------------------------------------------------------------- | -------------------------------------------------- | | Tailwind theme @reference (single, paths from src/css/) | css/editor-partials/tailwind-theme-reference.css | | Scoped .btn / .input-hover (not @utility) | css/editor-partials/utilities.css | | Google icons + mobile preview overrides | css/editor-partials/icons-and-mobile-preview.css | | Canvas selection, drag/drop, #viewport handles | css/editor-partials/canvas-interaction.css | | Shell typography on .pagehub-sdk-root, scrollbars, range thumbs | css/editor-partials/base-and-scrollbars.css | | #viewport / [data-renderer] containment | css/editor-partials/viewport-layout.css | | Toolbar inputs, buttons, sliders | css/editor-partials/toolbar-forms.css | | Third-party (e.g. Sketch color picker) | css/editor-partials/third-party.css | | HeadlessUI listbox (ph-select-*) | css/editor-partials/dropdowns.css | | Scroll/hover presets + css-* keyframes | css/styles.css (animation presets section) | | Sequential spotlight presets (chain/grid) | css/styles.css (spotlight presets section) |

Naming contract (integrators & contributors)

  • pagehub-* — Editor shell surface (e.g. pagehub-sdk-root). Prefer this prefix for new chrome-only classes.
  • ph-* — Supported shorthand today for dropdowns and motion utilities: ph-select-*, ph-anim-scroll, ph-hover-*, plus ph-in-view for scroll-triggered play state. External CSS that targets these may break if we rename them; a future pagehub-* migration would be one coordinated release with a changelog note.
  • data-* on canvas nodes — Treat as stable DOM protocol (persisted / editor behaviour). Do not rename without a data migration story.

Dual CSS in this repo

  • @pagehub/sdk/editor.css (SDK dist) — Full editor + Tailwind scan + chrome. Use when embedding the SDK.
  • styles/editor.css (Next app) — App-specific globals that overlap conceptually but are not the same artifact; keep SDK changes in packages/sdk/src/css/editor.css and packages/sdk/src/css/editor-partials/.

PageHub Cloud Features

Some editor features require a PageHub account or self-hosted backend:

| Feature | Requires Backend | Notes | | ---------------------------- | ---------------- | ------------------------------------------------------------- | | Drag-and-drop editor | No | Works standalone | | Viewer / static renderer | No | Works standalone | | AI content generation | Yes | Enable with ai: { enabled: true }, requires PageHub account | | Image uploads | Yes | Provide onMediaUpload callback or use PageHub CDN | | Domain settings | Yes | Requires PageHub Cloud | | Multi-tenant / user profiles | Yes | Requires PageHub Cloud |

The core editor and viewer work fully offline with just the onLoad/onSave callbacks.


Contributing

Contributions are welcome! See CONTRIBUTING.md for dev setup, architecture, and PR guidelines.

License

MIT