DOM Coverage Boundaries

PreviousNext

DOM coverage boundaries let Slate keep model content selectable and copyable when the matching editable DOM is intentionally not mounted. Use them for closed accordions, inactive tab panels, collapsed sections, and app-hidden element shells.

They are React rendering tools. The document content stays in the Slate value.

Render A Boundary

renderElement receives slots.contentBoundary. Wrap the hidden part of an element with a boundary and tell Slate which children it covers.

const renderElement = ({ attributes, children, element, slots }) => {
  if (element.type === 'section') {
    return (
      <section {...attributes}>
        {React.Children.toArray(children)[0]}
        {slots.contentBoundary({
          mounted: !element.collapsed,
          onMaterialize: () => openSection(element.id),
          renderPlaceholder: ({ materialize }) => (
            <button onClick={materialize} type="button">
              Show section
            </button>
          ),
          scope: { from: 1, type: 'children' },
          selectionPolicy: 'materialize',
        })}
      </section>
    )
  }
 
  return <p {...attributes}>{children}</p>
}
const renderElement = ({ attributes, children, element, slots }) => {
  if (element.type === 'section') {
    return (
      <section {...attributes}>
        {React.Children.toArray(children)[0]}
        {slots.contentBoundary({
          mounted: !element.collapsed,
          onMaterialize: () => openSection(element.id),
          renderPlaceholder: ({ materialize }) => (
            <button onClick={materialize} type="button">
              Show section
            </button>
          ),
          scope: { from: 1, type: 'children' },
          selectionPolicy: 'materialize',
        })}
      </section>
    )
  }
 
  return <p {...attributes}>{children}</p>
}

Use scope={{ type: 'children', from, to }} when the boundary covers child nodes. Use scope={{ type: 'self' }} when the whole element is hidden by app chrome.

Selection Policy

selectionPolicy controls keyboard and model selection through hidden content.

PolicyBehavior
skipMove selection outside the hidden range. Use it for closed UI chrome.
modelLet the model selection include hidden content without mounting it.
materializeMount the hidden content before Slate moves selection into it.

materialize calls onMaterialize({ boundary, reason, range, rangeRole }). Use that callback to open the accordion, activate the tab, or reveal the collapsed panel.

Copy Policy

copyPolicy controls clipboard output when a copied selection crosses hidden content.

PolicyBehavior
modelCopy the hidden Slate content from the editor value.
summaryCopy the boundary placeholder or summary content only.
excludeOmit the hidden content from the clipboard.
materializeMount the hidden content before copying and then copy normally.

model is the right default for document content. exclude is usually better for app-hidden wrappers, private metadata, and chrome-only shells.

Find Policy

findPolicy records who owns search for that hidden range.

PolicyBehavior
nativeBrowser find searches the mounted DOM only.
customThe app owns model search for hidden content.

Native browser find cannot search content that is not mounted in the DOM. If a product needs Cmd+F over collapsed content, build custom model search and set findPolicy="custom" on those boundaries.

Screen readers also traverse mounted DOM, not hidden Slate model children. If collapsed content must remain available to assistive technology, keep an accessible summary mounted, materialize the content from the boundary, or render an app-owned accessible representation outside the editable surface.

Boundary Identity

boundaryId is optional. Slate derives a stable editor-local boundary id from the rendered element and scope. Pass an explicit id when tests or diagnostics need a predictable name.

Boundary placeholders are runtime-owned non-editable DOM. Keep tab triggers, accordion buttons, and other app chrome outside the editable text flow so native selection does not grab UI labels instead of document text.

Boundaries Versus Content Roots

Use DOM coverage boundaries when content belongs to the current root but its DOM is hidden. Use content roots when an element owns another editable root, such as a synced block body or an editable card body.