@itzsa/page-builder
v0.2.0
Published
Drag-and-drop visual page builder — primitives, canvas, editor chrome, preview, author CSS/JS
Maintainers
Readme
@itzsa/page-builder
Drag-and-drop visual page builder for React (Elementor / Webflow / Puck class).
Authors compose pages from registered blocks. You own the data — persist structured Page JSON. Canvas, Preview, and Open Page share one React render path.
Full docs (site): /page-builder in this monorepo. Design authority: ARCHITECTURE-PAGE-BUILDER.md. Topic tree: docs/page-builder/.
Features
| Feature | Description |
| --- | --- |
| Block registry | Register React blocks (or use primitives) with fields + render |
| Page JSON | Persist a schema-validated document — not a one-off HTML dump |
| Render parity | Same render for canvas / preview / open |
| Localization | First-class i18nProps with host-configured locales |
| Author CSS / JS | Look from composers — no engine decorative skins |
| Motion Effects | Entrance + hover via block.motion (canvas / preview / open parity) |
| Visibility | Device, locale, publish, renderContext predicates |
| Data sources | Repeater + {{item.*}} via fetchDataSource |
| Feature toggling | capabilities + host UI flags |
Install
pnpm add @itzsa/page-builder zodPeers: react, react-dom (^18 || ^19).
import "@itzsa/page-builder/styles.css";Render the editor
import { useState } from "react";
import {
PageBuilder,
createRegistry,
registerPrimitives,
createDefaultLocaleConfig,
PAGE_SCHEMA_VERSION,
type Page,
} from "@itzsa/page-builder";
import "@itzsa/page-builder/styles.css";
const registry = createRegistry();
registerPrimitives(registry);
const localeConfig = createDefaultLocaleConfig();
const initialPage: Page = {
id: "home",
schemaVersion: PAGE_SCHEMA_VERSION,
revision: "1",
meta: { title: "Home" },
blocks: [],
};
export function Editor() {
const [page, setPage] = useState(initialPage);
const [locale, setLocale] = useState(localeConfig.defaultLocale);
return (
<PageBuilder
page={page}
onChange={setPage}
registry={registry}
localeConfig={localeConfig}
activeLocale={locale}
onActiveLocaleChange={setLocale}
onSave={(next) => {
void savePage(next);
}}
capabilities={{ allowCustomCss: true, allowCustomJs: false }}
/>
);
}Render the page
import {
RenderPage,
OpenPageView,
createRegistry,
registerPrimitives,
createDefaultLocaleConfig,
type Page,
} from "@itzsa/page-builder";
const registry = createRegistry();
registerPrimitives(registry);
const localeConfig = createDefaultLocaleConfig();
export function PageView({ page, locale }: { page: Page; locale: string }) {
return (
<RenderPage
page={page}
registry={registry}
localeConfig={localeConfig}
activeLocale={locale}
surface="open"
/>
);
}
export function PublishedPage({ page, locale }: { page: Page; locale: string }) {
return (
<OpenPageView
page={page}
registry={registry}
localeConfig={localeConfig}
activeLocale={locale}
/>
);
}Show page on your site (save → fetch → render)
- Save —
onSavepersistsPageJSON to your API (not HTML as source of truth). - Preview (optional) —
createPreviewSession+buildPreviewUrl→ another route loads withloadPreviewSession+OpenPageView(opaque id in URL only). - Public page — fetch JSON by slug/id, then mount
OpenPageViewwith the same registry as the editor.
// Public route — this is the component visitors see
const page = await getPageBySlug(slug); // your backend
return (
<OpenPageView
page={page}
registry={registry}
localeConfig={localeConfig}
activeLocale={localeConfig.defaultLocale}
/>
);Full guide: docs/page-builder/guides/show-page-on-site.md · live docs /page-builder#show-on-site.
Flex & Grid nesting
Drop Flex or Grid on the canvas, then drop other blocks into the dashed Empty / Drop here zone. Children are stored on block.children.
Palette filters
palette={{
hideCategories: ["other"],
hideBlocks: ["html", "repeater"],
}}Locales
import {
createDefaultLocaleConfig, // en + ne
createEnglishOnlyLocaleConfig,
createNepaliOnlyLocaleConfig,
createLocaleConfig,
} from "@itzsa/page-builder";Validate author CSS / JS on save
import { validateAuthorCode } from "@itzsa/page-builder";
const result = validateAuthorCode(page);
if (!result.ok) {
// reject — result.cssErrors / result.jsErrors
}License
MIT — Copyright (c) 2026 Suman Acharya.
Contributing & bugs
- CONTRIBUTING.md
- Report a bug
- SECURITY.md for vulnerabilities (private only)
