Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
  • Plate
  • Editor API
  • Editor Transforms
  • Node
  • Element
  • Text
  • Path
  • Point
  • Range
  • Location
  • Anchor
  • Selection
  • Document Change
  • DOM API
  • React Hooks
  • Plate Core
    • Plate Components
    • Plate Editor
    • Plate Plugin
    • Editor Context
    • Plate Controller
  • Plate Utils
  • Resizable

Plate Controller

PreviousNext

Connect shared React UI to the active Plate editor.

EditorController lets shared toolbars, panels, and inspectors resolve an editor outside a single EditorRoot subtree.

Usage

Wrap the shared UI and participating editors in EditorController. EditorContent automatically registers each editor for its mounted lifetime.

components/editor-shell.tsx
import {
  EditorRoot,
  EditorContent,
  EditorController,
  useCreateEditor,
  useOptionalEditor,
} from 'platejs/react';
 





















Editor ContextPlate Utils

On This Page

UsageEditor lookupCaptured interactionsAPI Reference
Build your editor
Production-ready AI template and reusable components.
Get all-access
export function EditorShell() {
const main = useCreateEditor({ id: 'main' });
const secondary = useCreateEditor({ id: 'secondary' });
return (
<EditorController>
<ActiveEditorLabel />
<EditorRoot editor={main}>
<EditorContent />
</EditorRoot>
<EditorRoot editor={secondary} primary={false}>
<EditorContent />
</EditorRoot>
</EditorController>
);
}
function ActiveEditorLabel() {
const editor = useOptionalEditor();
return <p>{editor ? `Active editor: ${editor.id}` : 'No editor selected.'}</p>;
}
components/editor-shell.tsx
import {
  EditorRoot,
  EditorContent,
  EditorController,
  useCreateEditor,
  useOptionalEditor,
} from 'platejs/react';
 
export function EditorShell() {
  const main = useCreateEditor({ id: 'main' });
  const secondary = useCreateEditor({ id: 'secondary' });
 
  return (
    <EditorController>
      <ActiveEditorLabel />
      <EditorRoot editor={main}>
        <EditorContent />
      </EditorRoot>
      <EditorRoot editor={secondary} primary={false}>
        <EditorContent />
      </EditorRoot>
    </EditorController>
  );
}
 
function ActiveEditorLabel() {
  const editor = useOptionalEditor();
 
  return <p>{editor ? `Active editor: ${editor.id}` : 'No editor selected.'}</p>;
}

Set primary on EditorRoot; it defaults to true. A view with primary={false} can become active when focused. IDs are application labels; one model and ID can have several mounted views.

Editor lookup

useEditor() returns the nearest provider’s selected editor and throws when no target exists. useOptionalEditor() uses the same selection and returns null without a provider or target.

  • Inside EditorContent, hooks use that exact editable view, including its root, DOM, and current read-only state.
  • Inside EditorRoot, outside its content, UI follows that Plate’s last focused view, then its first mounted view. A Plate without content provides its model.
  • Shared UI under the controller uses the last focused mounted view, then the first mounted primary view before focus or after target removal.
  • Nested EditorRoot and EditorController components scope their descendants independently.

Blur retains the active view for toolbar interaction. EditorContent owns registration and cleanup; only views with editable DOM participate in controller selection.

Captured interactions

Use EditorProvider to bind controls to an existing editor. For example, a popup can capture the value returned by useEditor() when it opens and keep that target until it closes.

import { EditorProvider } from 'platejs/react';
 
<EditorProvider editor={capturedEditor}>
  <Toolbar />
</EditorProvider>
import { EditorProvider } from 'platejs/react';
 
<EditorProvider editor={capturedEditor}>
  <Toolbar />
</EditorProvider>

EditorProvider accepts editor: Editor | null and children: React.ReactNode; it creates no editable view. React portals retain this context. Commands read the target’s current permissions. Read-only or unmounted targets reject writes, and captured interactions never select a replacement view automatically.

API Reference

EditorController accepts only children: React.ReactNode. The primary prop on EditorRoot controls initial and fallback eligibility.

For state and focus subscriptions, see Editor Context.