Plite documents are JSON trees. The editable content in each root is built from two node types:
Element nodes hold structure and contain child nodes.Text nodes hold string content and mark properties.The editor runtime can also act as the root ancestor for node APIs, but the
persisted document value is primary children, optional extra roots, and optional
meta from state fields. Save editor.read.value() when you need the whole document.
const documentValue = {
children: [
{
type: "paragraph",
children: [{ text: "A line of text!" }],
},
const documentValue = {
children: [
{
type: "paragraph",
children: [{ text: "A line of text!" }],
},
],
meta: {
"document.title": "Draft",
},
};The short editor value is still an array of block elements. Plite treats that array as the primary document.
const initialValue = [
{
type: "paragraph",
children: [{ text: "A line of text!" }],
},
];const initialValue = [
{
type: "paragraph",
children: [{ text: "A line of text!" }],
},
];See Document Meta for persistence and Roots for extra roots, content roots, and multi-root rendering.
Elements make up the structure of a Plite document. They are the nodes that are custom to your product domain.
interface Element {
children: Node[];
}interface Element {
children: Node[];
}A paragraph, quote, heading, link, image, or custom card is an element. Element properties are yours to define.
const paragraph = {
type: "paragraph",
children: [{ text: "Hello" }],
};
const link = {
type: "link",
url: "https://example.com",
children: [{ text: "Example" }],
};const paragraph = {
type: "paragraph",
children: [{ text: "Hello" }],
};
const link = {
type: "link",
url: "https://example.com",
children: [{ text: "Example" }],
};All elements have children. Void elements still keep a text child in the
model so selection, marks, copy/paste, and normalization have a stable location.
const image = {
type: "image",
url: "https://example.com/image.png",
children: [{ text: "" }],
};const image = {
type: "image",
url: "https://example.com/image.png",
children: [{ text: "" }],
};Text nodes are leaves. They contain the string plus any mark properties.
interface Text {
text: string;
}interface Text {
text: string;
}Marks are normal custom properties on text nodes.
const boldText = {
text: "Bold",
bold: true,
};const boldText = {
text: "Bold",
bold: true,
};Plite splits adjacent text into leaves for rendering when decorations or marks change, but the document model is still text nodes inside elements.
Use namespaced node reads for tree queries. type selects structural node
types. match adds a predicate.
const callout = editor.read.nodes.find({
type: "callout",
});
const tableNode = editor.read.nodes.find({
type: ["table", "table_cell"],
});const callout = editor.read.nodes.find({
type: "callout",
});
const tableNode = editor.read.nodes.find({
type: ["table", "table_cell"],
});An array selects any listed type. Use a predicate when the condition is computed or when it should narrow the returned node type.
When component code already owns a text or element node, pass that node to a
public at option instead of resolving its path first. See
Locations.
Elements default to block behavior. A block element can only be a sibling of other block elements.
Inline elements live in text flow. Links, mentions, and inline equations are typical inline elements. Inline elements can be siblings with text nodes and other inline elements.
Declare behavior and child grammar in the schema.
import { defineExtension, property, schema } from "@platejs/plite";
const Links = defineExtension("links", {
schema: {
elements: {
link: {
content: schema.content.text({ default: "text", min: 1 }),
inline: true,
properties: {
url: property.string(),
},
},
},
},
});import { defineExtension, property, schema } from "@platejs/plite";
const Links = defineExtension("links", {
schema: {
elements: {
link: {
content: schema.content.text({ default: "text", min: 1 }),
inline: true,
properties: {
url: property.string(),
},
},
},
},
});Non-inline elements automatically belong to the compiler-owned block group.
Inline elements declare inline: true and automatically belong to inline.
Use groups only for application-specific semantic groupings.
Void elements render visible content without making their own children directly editable. Use voids for images, embeds, mentions, horizontal rules, and other objects that behave as a single editor unit.
Declare void behavior in an extension schema.
import { defineExtension, property } from "@platejs/plite";
const Images = defineExtension("images", {
schema: {
elements: {
image: {
properties: {
url: property.string(),
},
void: "block",
},
},
},
});import { defineExtension, property } from "@platejs/plite";
const Images = defineExtension("images", {
schema: {
elements: {
image: {
properties: {
url: property.string(),
},
void: "block",
},
},
},
});The React runtime owns the hidden editable anchor and selection shell for voids.
App renderers return the visible content through Editable's renderVoid
prop. See Element API for a
complete rendering example.
Plite keeps document trees valid while transaction writes run.
See Schema for declarations, compiled validation, and fitting. See Normalizing for semantic corrections that run after schema-valid writes.