Element API

PreviousNext

Element objects are a type of Node in a Slate document that contain other Element nodes or Text nodes.

interface Element {
  children: Node[]
}
interface Element {
  children: Node[]
}

Element Behavior Types

Element nodes behave differently depending on the Slate editor's schema. An element can be:

  • "block" or "inline" as defined by state.schema.isInline(element)
  • either "void" or "not void" as defined by state.schema.isVoid(element)

Block vs. Inline

A "block" element can only be siblings with other "block" elements. An "inline" node can be siblings with Text nodes or other "inline" elements.

Void vs Not Void

In a not "void" element, Slate handles the rendering of its children (e.g. in a paragraph where the Text and Inline children are rendered by Slate). In a "void" element, Slate owns the DOM shell and selection anchor while app code renders only the visible content.

Voids That Support Marks

Some void elements are effectively stand-ins for text, such as with the Mentions example, where the mention element renders the character's name. Users might want to format Void elements like this with bold, or set their font and size, so state.schema.markableVoid(element) tells Slate whether or not to apply Marks to the text children of void elements.

Rendering Void Elements

Void elements still contain a text child in the Slate document model so selection and marks have a stable model location. The React runtime renders the hidden anchor and browser shell for you. App renderers should use Editable's renderVoid prop and return visible content only.

Typical rendering code will resemble this image element:

import type { RenderVoidProps } from '@platejs/slate-react'
 
type ImageElement = {
  type: 'image'
  url: string
  children: [{ text: '' }]
}
 
const Image = ({ element }: RenderVoidProps<ImageElement>) => {
  return <img alt="" src={element.url} />
}
 
<Editable
  renderVoid={(props) => {
    switch (props.element.type) {
      case 'image':
        return <Image {...props} />
      default:
        return null
    }
  }}
/>
import type { RenderVoidProps } from '@platejs/slate-react'
 
type ImageElement = {
  type: 'image'
  url: string
  children: [{ text: '' }]
}
 
const Image = ({ element }: RenderVoidProps<ImageElement>) => {
  return <img alt="" src={element.url} />
}
 
<Editable
  renderVoid={(props) => {
    switch (props.element.type) {
      case 'image':
        return <Image {...props} />
      default:
        return null
    }
  }}
/>

For a "markable" void such as a mention element, marks on the text child can still be used to determine how the visible content is rendered. Selection UI is an opt-in target subscription:

import { useElementSelected, type RenderVoidProps } from '@platejs/slate-react'
 
type MentionElement = {
  type: 'mention'
  character: string
  children: [{ bold?: true; italic?: true; text: '' }]
}
 
const Mention = ({ element }: RenderVoidProps<MentionElement>) => {
  const selected = useElementSelected()
  const text = element.children[0] ?? {}
  const style = {
    padding: '3px 3px 2px',
    margin: '0 1px',
    verticalAlign: 'baseline',
    display: 'inline-block',
    borderRadius: '4px',
    backgroundColor: '#eee',
    fontSize: '0.9em',
    boxShadow: selected ? '0 0 0 2px #B4D5FF' : 'none',
  }
  if (text.bold) {
    style.fontWeight = 'bold'
  }
  if (text.italic) {
    style.fontStyle = 'italic'
  }
  return (
    <span
      data-cy={`mention-${element.character.replace(' ', '-')}`}
      style={style}
    >
      @{element.character}
    </span>
  )
}
import { useElementSelected, type RenderVoidProps } from '@platejs/slate-react'
 
type MentionElement = {
  type: 'mention'
  character: string
  children: [{ bold?: true; italic?: true; text: '' }]
}
 
const Mention = ({ element }: RenderVoidProps<MentionElement>) => {
  const selected = useElementSelected()
  const text = element.children[0] ?? {}
  const style = {
    padding: '3px 3px 2px',
    margin: '0 1px',
    verticalAlign: 'baseline',
    display: 'inline-block',
    borderRadius: '4px',
    backgroundColor: '#eee',
    fontSize: '0.9em',
    boxShadow: selected ? '0 0 0 2px #B4D5FF' : 'none',
  }
  if (text.bold) {
    style.fontWeight = 'bold'
  }
  if (text.italic) {
    style.fontStyle = 'italic'
  }
  return (
    <span
      data-cy={`mention-${element.character.replace(' ', '-')}`}
      style={style}
    >
      @{element.character}
    </span>
  )
}

Static methods

Retrieval methods

ElementApi.matches(element: Element, props: Partial<Element>) => boolean

Check if an element matches a set of props. Note: This checks custom properties, but it does not ensure that any children are equivalent.

Check methods

ElementApi.isAncestor(value: unknown) => value is Ancestor

Check if a value implements the 'Ancestor' interface.

ElementApi.isElement(value: unknown) => value is Element

Check if a value implements the Element interface.

ElementApi.isElementList(value: unknown) => value is Element[]

Check if a value is an array of Element objects.

ElementApi.isElementProps(props: unknown) => props is Partial<Element>

Check if a value is an object that can be used as partial Element props.

ElementApi.isElementType<T Extends Element>(value: unknown, elementVal: string, ElementKey: string = 'type'): value is T

Check if a value implements the Element interface and has elementKey with selected value. Default it check to type key value