Dollie Editor SDK guide
Global Sections
Save a section once, place it on many pages as a linked copy, and update every page by editing it once
A Global Section is one section a host stores once and places on many Pages. Editing the global updates every Page that links it. It suits blocks a site repeats: a closing call to action, a use-case row, a "choose how to start" chooser.
A linked copy, not a reference
A Page never points at a global instead of a Section. It keeps a complete, renderable Section plus a link:
{
"component": "shared-photo-split-with-cta",
"props": { "heading": "Which matter work would you prepare first?", "cta": { "label": "Talk to us", "href": "/contact" } },
"divider": true,
"global": { "id": "closing-cta", "local": ["heading"] }
}
componentand every prop not listed inlocalfollow the global and are read-only on the Page.- The props named in
localbelong to this Page. Only whole top-level fields can be listed. - Framing (
theme,border,divider,className) always belongs to the placement. - Unknown keys inside
globalare ignored, so the link can grow later without a format change.
Because the snapshot is always complete, the global key is additive: _version does not change, every existing reader (renderer, templates, clipboard, previews, live sites) keeps working, and a missing global leaves a working Section.
Resolution
resolveGlobalLinks(definition, lookup, fieldsFor?) refreshes every linked Section from its global and reports each link as current, refreshed or missing. A missing global leaves the snapshot untouched. A local key the global's component no longer declares is dropped. Staleness is a content comparison that ignores key order, treats {} and [] as equal, and compares numbers by value, so a TypeScript and a PHP host give the same answer.
import { resolveGlobalLinks } from '@dollie_ai/editor';
const { definition, reports, changed } = resolveGlobalLinks(page, (id) => globals[id]?.section ?? null);
Helpers built on it: linkedSectionFromGlobal (Insert), copyFromGlobal (Insert as copy), linkGlobalSection (link in place after saving a global), detachGlobalSection, setGlobalLocalField (the "Customise on this page" toggle), globalSectionUsage, stripGlobalLinks (plain copies for readers that must not carry links) and normalizeGlobalLinks (applied Copilot proposals). The Laravel adapter's Dollie\Editor\Support\GlobalSectionLinks is the PHP twin.
Validation
validatePageConfig and the adapter's PageDefinitionValidator check only the link's structure and report invalid_global:
| Case | Path |
|---|---|
global is not an object, or id is not a readable id (^[A-Za-z0-9][A-Za-z0-9_-]*$, up to 200 characters) |
sections.N.global |
local is not a list of unique, non-empty strings |
sections.N.global.local |
a local key is not a declared top-level field (checked when fields are known) |
sections.N.global.local.K |
Whether the global exists, or has changed, is a status from resolveGlobalLinks, never a validation error.
The editor
Pass the host's globals through the runtime:
<PageBuilderEditorProvider
runtime={{
catalog,
sections,
globalSections: {
items: [{ id: 'closing-cta', name: 'Closing CTA', section, usageCount: 7, editUrl }],
create: async (name, section) => saveGlobalOnServer(name, section), // resolves to { id, name, section }
},
}}>
- Add section shows a Global sections group first, each with Insert (linked) and Insert as copy.
- A linked Section shows a Global · Closing CTA chip on the canvas and a link icon in Layers. The inspector shows Global section · used on N pages with Edit global section and Detach; each top-level field has a Customise on this page toggle, and fields that follow the global are read-only (inline canvas editing reveals the field instead).
- The section menu offers Save as global section… (when
createis supplied), Edit global section and Detach from global. - Opening a Page with stale links refreshes them as one undoable change, Updated linked global sections, so the Page shows unsaved changes and nothing changes silently.
- Pasting keeps a link only when this host has that global and its component matches; otherwise the Section lands as a local copy ("Pasted as a local copy").
The editor state enforces the lock: prop writes to fields that follow the global are refused, and replacing a linked Section (AI fill, Copilot) keeps the link and changes only page-owned fields and framing.
create must resolve to the new global; add it to items so the chip and inspector know its name. Storage, ids, permissions, propagation and publishing stay host-owned: see Authoring Library.
Copilot
useEditorCopilot({ globalSections }) adds sectionGlobals (per index: { id, local, props } or null) and the available globalSections to the page_context payload, and applied propose_page results resolve their links (unknown ids become local copies). createProposalOperations({ pageSections }) refuses a propose_section_edit that changes a linked Section's shared content with global_section_linked; globalSectionsRuleBlock() is the prompt rule (PHP: CopilotGrounding::globalSectionsBlock()).