Interfaces

PreviousNext

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.