@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.
Maintainers
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 # peersUsage
'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
homeHrefif set) - Page title crumb in the centre
- Right side: optional
headerActions, optional "New page" (ifonCreatePageset), optional "View live" (iflivePathset), 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 —
savePageis your line of integration. - Both
loadPageandsavePageshould throw on failure. The editor turns errors into AntD message toasts. onCreatePageis a callback, not an adapter function — typically justrouter.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.
