Slate documents are plain JSON objects with a small set of structural interfaces. Your app owns the product-specific fields.
Text
Text nodes contain text plus optional mark properties.
interface Text {
text: string
}interface Text {
text: string
}const text = {
text: 'Bold',
bold: true,
}const text = {
text: 'Bold',
bold: true,
}Slate treats bold as your data. The editor only requires the text string.
Element
Elements contain child nodes and any product fields your schema needs.
interface Element {
children: Node[]
}interface Element {
children: Node[]
}const paragraph = {
type: 'paragraph',
children: [{ text: 'Body' }],
}
const link = {
type: 'link',
url: 'https://example.com',
children: [{ text: 'Example' }],
}const paragraph = {
type: 'paragraph',
children: [{ text: 'Body' }],
}
const link = {
type: 'link',
url: 'https://example.com',
children: [{ text: 'Example' }],
}The type and url fields are your API. Slate sees them, preserves them, and
passes them to renderers and transforms, but your app decides what they mean.
const LinkElement = ({ attributes, children, element }) => {
return (
<a {...attributes} href={element.url}>
{children}
</a>
)
}const LinkElement = ({ attributes, children, element }) => {
return (
<a {...attributes} href={element.url}>
{children}
</a>
)
}Helper Namespaces
Runtime values use plain objects. Static helper functions live on *Api
namespaces such as NodeApi, ElementApi, TextApi, PathApi, PointApi,
and RangeApi.
import { ElementApi, NodeApi, RangeApi } from '@platejs/slate'
const string = NodeApi.string(element)
const isElement = ElementApi.isElement(value)
const collapsed = RangeApi.isCollapsed(range)import { ElementApi, NodeApi, RangeApi } from '@platejs/slate'
const string = NodeApi.string(element)
const isElement = ElementApi.isElement(value)
const collapsed = RangeApi.isCollapsed(range)Use the helper namespace when you need structural checks, path math, range direction, or text extraction outside an editor read/update callback.
Inside editor.read(...) and editor.update(...), prefer the grouped runtime
helpers because they understand the current root, schema, and transaction.
const linkEntry = editor.read((state) =>
state.nodes.above({
match: (node) => ElementApi.isElement(node) && node.type === 'link',
})
)const linkEntry = editor.read((state) =>
state.nodes.above({
match: (node) => ElementApi.isElement(node) && node.type === 'link',
})
)Domain Helpers
Keep domain helpers as normal functions or expose them from an extension when they need editor state.
import { ElementApi, type Element } from '@platejs/slate'
type ImageElement = Element & {
type: 'image'
url: string
}
const isImageElement = (value: unknown): value is ImageElement =>
ElementApi.isElement(value) &&
value.type === 'image' &&
typeof value.url === 'string'import { ElementApi, type Element } from '@platejs/slate'
type ImageElement = Element & {
type: 'image'
url: string
}
const isImageElement = (value: unknown): value is ImageElement =>
ElementApi.isElement(value) &&
value.type === 'image' &&
typeof value.url === 'string'Use Extensions when a helper should become a typed state,
tx, or api namespace installed on the editor.