Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
    • Overview
    • Why This Fork
    • Examples
    • Installing Plite
    • Adding Event Handlers
    • Defining Custom Elements
    • Applying Custom Formatting
    • Executing Commands
    • Saving to a Database
    • Canonical Change Substrate
    • Improving Performance
    • Interfaces
    • Nodes
    • Locations
    • Transforms
    • Document Changes
    • Commands
    • Editor
    • Plugins
    • Rendering
    • Serializing
    • Normalizing
    • Using TypeScript
    • Roots
    • Document State
    • Editing Behavior
    • Selection And DOM
    • Clipboard And Paste
    • Decorations, annotations, and widgets
    • Schema
    • Anchor API
    • Location API
    • Path API
    • PointEntry API
    • Point API
    • Range API
    • Selection API
    • Location Types APIs
    • Span API
    • Editor
    • Element API
    • NodeEntry API
    • Node API
    • Node Types APIs
    • Text API
    • Debug Value Scrubbing
    • Transforms API
    • Plite DOM
    • History Editor API
    • History Plugin Setup
    • History
    • Plite History
    • Plite Hyperscript
    • Plite PaginationExperimental
    • Annotations
    • DOM Coverage Boundaries
    • Editable Component
    • External Text Views
    • Plite React Event Handling
    • Virtualized Rendering
    • Plite React Hooks
    • React Editor Setup
    • React Editor
    • Plite React
    • Plite Component
    • Plite Yjs
    • Plite
    • Migration
    • Contributing
    • FAQ
    • Resources

Nodes

PreviousNext

Model documents with Element and Text nodes across primary and extra roots.

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.

Value shape

The editor runtime can also act as the root ancestor for node APIs, but the persisted document value contains primary children, optional extra roots, optional records, 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!"





InterfacesLocations

On This Page

Value shapeElementTextFinding nodesBlocks and inlinesVoidsNode tree rules
Build your editor
Production-ready AI template and reusable components.
Get all-access
}],
},
],
meta: {
"document.title": "Draft",
},
};
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.

Element

Elements make up the structure of a Plite document. They are the nodes that are custom to your product domain.

interface Element {
  readonly children: readonly Descendant[];
  readonly type: string;
  readonly [key: string]: unknown;
}
interface Element {
  readonly children: readonly Descendant[];
  readonly type: string;
  readonly [key: string]: unknown;
}

A paragraph, quote, heading, link, image, or custom card is an element. Every element requires a type string; your app defines its custom properties.

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

Text nodes are leaves. They contain the string plus any mark properties.

interface Text {
  readonly text: string;
  readonly [key: string]: unknown;
}
interface Text {
  readonly text: string;
  readonly [key: string]: unknown;
}

Marks are normal custom properties on text nodes.

const boldText = {
  text: "Bold",
  bold: true,
};
const boldText = {
  text: "Bold",
  bold: true,
};

Plite may split one text node into rendered leaf segments at Decoration boundaries, but the document model remains text nodes inside elements.

Finding nodes

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.

Blocks and inlines

Elements default to block behavior. The parent schema determines which block, inline, and text children can be siblings.

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 { definePlugin, property, schema } from "plitejs";
 
const Links = definePlugin("links", {
  schema: {
    elements: {
      link: {
        content: schema.content.text({ default: "text", min: 1 }),
        inline: true,
        properties: {
          url: property.string(),
        },
      },
    },
  },
});
import { definePlugin, property, schema } from "plitejs";
 
const Links = definePlugin("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.

Voids

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 plugin schema.

import { definePlugin, property } from "plitejs";
 
const Images = definePlugin("images", {
  schema: {
    elements: {
      image: {
        properties: {
          url: property.string(),
        },
        void: "block",
      },
    },
  },
});
import { definePlugin, property } from "plitejs";
 
const Images = definePlugin("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.

Node tree rules

Plite keeps document trees valid while transaction writes run.

  • Roots contain block elements.
  • Element children follow the schema content grammar, which can admit mixed block and inline/text content.
  • The schema defines required children through minimum counts and defaults.
  • Text nodes merge with adjacent text nodes that have matching marks.
  • Inline and void elements get empty text spacing where selection needs it.

See Schema for declarations, compiled validation, and fitting. See Normalizing for semantic corrections that run after schema-valid writes.