Page Builder
<zn-page-builder> | ZnPageBuilder
A config-driven page composer: a palette of predefined section types, a linear canvas of section cards, and an inspector for editing each section’s content.
The Page Builder composes pages from predefined section types the host application
provides. Editors pick sections from the palette, order them on the canvas, and edit each section’s content
in the inspector — the output is a plain JSON config (PageState) the host persists and renders
however it likes.
Section types are declared as <template type slot="config"> children. The template’s
attributes (type, label, icon, icon-library,
color, category, description) define the palette entry; its content
is stamped into the inspector when a section of that type is selected. Controls are bound by their
name attribute: values prefill from the section’s data and write back on change /
zn-change.
<zn-page-builder id="kb-homepage-demo" heading="KB Homepage" subheading="Last updated 7th July 2026" auto-save style="height: 560px"> <zn-button slot="header-left" icon="refresh-cw@lu" panel-bg onclick="this.closest('zn-page-builder').undo()">Undo</zn-button> <zn-button slot="header-right" icon="check@lu" color="primary" onclick="alert(JSON.stringify(this.closest('zn-page-builder').state, null, 2))">Save All Changes</zn-button> <template type="hero" slot="config" label="Hero" icon="star" category="Headers" description="Large banner with optional search"> <zn-input name="title" label="Title"></zn-input> <zn-input name="subtitle" label="Subtitle"></zn-input> </template> <template type="article-list" slot="config" label="Article List" icon="list" category="Content" description="A list of KB articles"> <zn-input name="limit" type="number" label="Max articles"></zn-input> </template> </zn-page-builder>
Listen for zn-page-change to persist the config, or read/write the state property.
Types can also be registered programmatically via sectionTypes /
registerSectionTypes(), including a renderConfig(section, update) callback for
inspector bodies that need real logic.
JavaScript API
-
state— get/set the currentPageState. The getter returns a deep copy; the setter replaces the state wholesale (like theconfigattribute, it does not emitzn-page-change). -
addSection(type, index?)— insert a new section of a registered type (default: at the end). -
addSectionToSlot(type, containerId, slotIndex)— insert into a container’s empty slot, honouring itsacceptslist. Returns the new section, ornullif not allowed. -
undo()/redo()— step through edit history (bounded at 50 entries). There is no built-in toolbar: wire these to your own header buttons or keyboard shortcuts. -
registerSectionType(type)/registerSectionTypes(types)— programmatic registration, equivalent to slotting templates. Registration is additive — removing an entry fromsectionTypeslater does not unregister it. -
restoreAutoSave()— load the auto-saved draft, if one exists within its 1-day TTL (returnsfalseotherwise). See Saving below.
Saving
Wire your own save action into the header slots (rendered only when filled, as in the flow builder) and read
state — or persist on every zn-page-change:
<zn-page-builder id="kb-home" heading="KB Homepage" auto-save> <zn-button slot="header-right" color="primary" id="save-page">Save All Changes</zn-button> <!-- templates… --> </zn-page-builder> <script> const builder = document.querySelector('#kb-home'); document.querySelector('#save-page').addEventListener('click', () => { fetch('/api/pages/home', {method: 'PUT', body: JSON.stringify(builder.state)}); }); </script>
Add the auto-save attribute to also snapshot the page into localStorage, keyed by the builder’s
id (falling back to its heading), with a 1-day TTL — identical to
the flow builder’s auto-save:
<zn-page-builder id="kb-home" auto-save></zn-page-builder> <!-- every 5 minutes --> <zn-page-builder id="kb-home" auto-save="2"></zn-page-builder> <!-- every 2 minutes -->
Without the attribute nothing is saved, and an empty page is never written. While auto-save is on, a status
pill in the canvas’s bottom-left flashes “Auto-saved” as each snapshot lands and otherwise shows how long
ago the last one happened. When a page is loaded (config / state) that differs
from a fresh auto-saved draft, a banner offers to restore the draft — or call
restoreAutoSave() yourself.
The config
Every edit emits zn-page-change with the full page state (event.detail.state, also
readable via the state property) — plain JSON the host persists and later feeds back in through
the config attribute. Sections appear in page order; container sections carry a
children array sized to their slot count, with null for empty slots:
{ "sections": [ { "id": "s-mc41z-0", "type": "hero", "data": {"title": "Help Centre", "showSearch": true} }, { "id": "s-mc42a-1", "type": "article-grid", "label": "Popular articles", "data": {"title": "Popular"}, "children": [ {"id": "s-mc42h-2", "type": "article-tile", "data": {"article": "art_42"}}, {"id": "s-mc42p-3", "type": "article-tile", "data": {"article": "art_7"}}, null, null, null, null ] }, { "id": "s-mc43b-4", "type": "article-list", "data": {"articles": ["art_1", "art_3"]} } ] }
Container tiles
A section type with a slots attribute becomes a full-row container: its card renders a 3-column
grid of that many child slots beneath it. Drag sections from the palette into empty cells, drag children
between cells to reorder, or out onto the page. accepts restricts which types
the slots take. Containers can’t be placed inside other containers, and slot contents persist as
children on the section (empty slots are null).
<zn-page-builder heading="KB Homepage" style="height: 560px" config='{"sections":[{"id":"g1","type":"article-grid","data":{}}]}'> <template type="article-grid" slot="config" label="Article Grid" icon="grid_view" category="Layout" description="A 3x2 grid of article tiles" slots="6" accepts="article-tile"> <zn-input name="title" label="Grid title"></zn-input> </template> <template type="article-tile" slot="config" label="Article" icon="article" category="Content" description="A single article tile"> <zn-input name="article" label="Article id"></zn-input> </template> </zn-page-builder>
List sections
Sections that show a set of existing items (categories, articles, …) reference them by id: bind a
multi-select by name and the chosen ids persist as an array in the section’s data (e.g.
"data": {"articles": ["art_42", "art_7"]}). Options can be inlined as below, or loaded from a
backend with <zn-data-select multiple>.
<zn-page-builder heading="KB Category Page" style="height: 420px"> <template type="article-list" slot="config" label="Article List" icon="list" description="Shows a chosen set of KB articles"> <zn-select multiple name="articles" label="Articles to show"> <zn-option value="art_1">How to reset your password</zn-option> <zn-option value="art_2">Billing FAQ</zn-option> <zn-option value="art_3">Getting started guide</zn-option> </zn-select> </template> </zn-page-builder>
Importing
If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.
To import this component from the CDN using a script tag:
<script type="module" src="https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.29/dist/components/page-builder/page-builder.js"></script>
To import this component from the CDN using a JavaScript import:
import 'https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.29/dist/components/page-builder/page-builder.js';
To import this component using a bundler:
import '@kubex/zinc/dist/components/page-builder/page-builder.js';
Slots
| Name | Description |
|---|---|
config
|
<template type="…"> declarations; never displayed. Each template’s attributes
(type, label, icon, icon-library, color, category, description, slots, accepts) declare a palette
entry and its content declares the inspector form for that type. slots makes the
section a container with that many child slots; accepts is a comma-separated list of
the type keys its slots allow.
|
header-left
|
Actions shown on the left of the header bar. |
header-right
|
Actions shown on the right of the header bar. |
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
config
|
The page state as a JSON string. Parsed on set; invalid JSON is ignored with a warning. |
string
|
''
|
|
sectionTypes
|
Section types to make available, registered into the internal registry. |
PageSectionType[]
|
[]
|
|
paletteCollapsed
palette-collapsed
|
Collapses the left palette. Auto-set when the builder becomes narrow. |
|
boolean
|
false
|
inspectorCollapsed
inspector-collapsed
|
Collapses the inspector while a section is selected. |
|
boolean
|
false
|
autoSave
auto-save
|
Auto-save the page to localStorage (1-day TTL). Omit to disable. A bare
auto-save saves every 5 minutes; a numeric value sets the interval in minutes
(auto-save="2"). Restore with restoreAutoSave().
|
number | null
|
null
|
|
state
|
Replaces the page state wholesale (does not emit zn-page-change). |
PageState
|
- | |
restoreAutoSave
|
Load the auto-saved page, if one exists within the 1-day TTL. | - | - | |
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
| Name | React Event | Description | Event Detail |
|---|---|---|---|
zn-page-change |
|
Emitted whenever the page state changes. event.detail.state is the new PageState.
|
- |
zn-page-selection-change |
|
Emitted when the selected section changes. event.detail.sectionId. |
- |
Learn more about events.
Methods
| Name | Description | Arguments |
|---|---|---|
addSection() |
Adds a section of a registered type at index (default: end). Returns null for unknown
types.
|
type: string, index: number
|
addSectionToSlot() |
Adds a new section of a registered type into a container’s slot. Returns null if not allowed. |
type: string, containerId: string, slotIndex: number
|
Learn more about methods.
Parts
| Name | Description |
|---|---|
base |
The grid wrapper. |
header |
The full-width header action bar (only rendered when header slots are filled). |
palette |
The left palette panel. |
canvas |
The centre section-card canvas. |
inspector |
The right panel while a section is selected. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<zn-collapsible><zn-example><zn-icon><zn-input><zn-page-palette-item><zn-page-section-card><zn-tooltip>