Dollie Editor SDK guide

Persistence and Dynamic Data

Store page definitions in your application and safely resolve backend data into published pages

Dollie Editor separates editable Page content from host-owned application data.

The Page definition is portable JSON. Your application decides where it lives and which dynamic values it may reference.

Documents wrap Page definitions

A BuilderDocument can contain:

  • id;
  • definition;
  • revision;
  • metadata;
  • capabilities.

The editor changes definition. The surrounding document helps the host manage identity, concurrency, and permissions.

Transport connects the editor to the host

TransportClient describes the operations the editor may call:

  • load;
  • save;
  • publish;
  • validate;
  • optional revision history;
  • optional binding and AI-fill operations.

The transport does not prescribe HTTP, Laravel, or a database.

Revision is not wire version

These two values solve different problems:

  • PageConfig._version identifies the JSON wire-format version.
  • BuilderDocument.revision identifies the current content revision and can prevent stale saves.

The wire version changes only for breaking format changes. The revision can change on every save.

Dynamic data uses bindings

Some values should come from the backend rather than be copied into the Page:

  • the current course title;
  • an instructor profile;
  • a list of upcoming sessions;
  • a tenant's subscription plans;
  • recent activity.

A binding describes the allowed source and lookup. The host resolves it with tenant-aware context before rendering.

{
  "$binding": {
    "source": "course",
    "kind": "item",
    "field": "title"
  },
  "fallback": "Course title"
}

The fallback keeps the editor and published page understandable when data is unavailable.

Bindings are an allow-list

Do not expose a generic database query language.

The host registers binding providers that define:

  • public fields;
  • allowed collection projections;
  • filters and sorts;
  • maximum limits;
  • tenant scope.

Resolution happens on the server with application context.

Live data inside a sentence

A binding replaces a whole value. When live data has to sit inside authored copy — Welcome to {{academy.name}}, book now — the host owns the token format and resolves it server-side before render, so the SDK never sees a token.

The editor helps the author write one. Pass a fieldTokens handle to PageBuilderEditor and mount FieldTokenPicker; every text field then shows an "Insert live data" button that places the chosen token at the caret:

const [tokenTarget, setTokenTarget] = useState<FieldTokenTarget | null>(null);

<PageBuilderEditor fieldTokens={{ ask: setTokenTarget }} … />
<FieldTokenPicker target={tokenTarget} groups={groups} onClose={() => setTokenTarget(null)} />

groups is the host's own list of { token, label, description, value, status }. The SDK never invents a spelling, and the same list should feed every other surface (a panel, an AI tool) so they cannot diverge. The handle is gated on the bindings capability, like the binding picker.

Keep editable content and live data distinct

Use a normal Field when the Editor user owns the value.

Use a binding when the application owns the value and the Page only chooses where to display it.

This distinction prevents copied data from becoming stale and keeps permissions clear.