EditorController lets shared toolbars, panels, and inspectors resolve an editor
outside a single EditorRoot subtree.
Wrap the shared UI and participating editors in EditorController.
EditorContent automatically registers each editor for its mounted lifetime.
import {
EditorRoot,
EditorContent,
EditorController,
useCreateEditor,
useOptionalEditor,
} from 'platejs/react';
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.
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.
EditorContent, hooks use that exact editable view, including its root, DOM, and current read-only state.EditorRoot, outside its content, UI follows that Plate’s last focused view, then its first mounted view. A Plate without content provides its model.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.
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.
EditorController accepts only children: React.ReactNode. The primary prop on EditorRoot controls initial and fallback eligibility.
For state and focus subscriptions, see Editor Context.