Skip to Content
PackagesCollaborationCustom Schemas

Custom Schemas

The collaboration server checks every edit against a schema and stores documents compacted against it, so the server and every editor must build the same schema, or one the server only extends. This page covers building that schema once and giving it to both sides.

One source of props

Keep the stesuraSchema props in one module of your own and import it on both sides:

// schema-props.ts import type { StesuraSchemaProps } from "@stesura/core/schema"; export const schemaProps = { nodes: { legal_notice: legalNoticeSpec }, // No pictures at all; see "Excluding a node". excludeNodes: ["image", "image_anchored"], } satisfies StesuraSchemaProps;
// The editor const schema = stesuraSchema(schemaProps); const { editorState, dispatch, pluginFactory } = useCollabEditor({ schema });

Removing core nodes works by whole families, and every feature that would create them steps aside (no toolbar button, refused paste and drop, skipped DOCX pictures). See Excluding a node.

Hosting the backend in your app

When the backend runs in your own Next or Node process, pass it the same schema:

const schema = stesuraSchema(schemaProps); export const backend = createCollabBackend({ schema, store: postgresStore({ url, schema }), broadcast });

The collab server image

A prebuilt image cannot import your code, so it takes the props from a module at COLLAB_SCHEMA_MODULE, exporting schema. The image builds the schema from its own copy of the editor core, so the module holds plain specs: bundle schema-props.ts (and any Stesura extension /schema subpath you use) into one .mjs and mount it at /app/schema.mjs. Without the module the image serves core plus the Mermaid, automation and e-sign nodes.

A module that is wrong (an unknown key, specs that do not build) stops the server at startup with the key named. The served schema’s fingerprint is logged. The on-prem bundle ships examples/schema-module.mjs.

Several schemas on one server

Export schemas and defaultSchema instead of schema from the module (or pass them to createCollabBackend when hosting in your app). Each document keeps the schema it was created under: create it with a name (addDoc({ ..., schemaName: "forms" }) on the room provider), or it gets the default. The room manifest names each document’s schema, so the editor can build the matching one:

// schema.mjs export const schemas = { docs: { schema: docsProps }, forms: { schema: formsProps }, }; export const defaultSchema = "docs";

Changing the schema

Editors send their schema’s fingerprint with every document request. When it differs, the server accepts the editor only if it has merely added nodes or marks; any other difference stops the editor with docStatus: "schema-mismatch". Deploy the server before the editors. The full rules are in schema compatibility.

Last updated on