The Plate editor exposes two main method surfaces: editor.api for reads and
services, and editor.update for commands that change editor state. In React,
pick the editor hook by how often the component should re-render.
| Need | Use |
|---|---|
| Read the required editor inside callbacks. | useEditor() |
| Re-render from an editor-derived value. | useEditorSelector(selector, options?) |
| Re-render from immutable state. |
useEditorState(selector, options?)| Render while a controller may have no editor. | useActiveEditor() and handle null. |
import { useEditor, useEditorSelector } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function BoldButton() {
const editor = useEditor();
const hasSelection = useEditorSelector((editor) =>
Boolean(editor.read.selection())
);
return (
<Button
disabled={!hasSelection}
onClick={() => editor.update((tx) => tx.marks.toggle('bold'))}
>
Bold
</Button>
);
}import { useEditor, useEditorSelector } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function BoldButton() {
const editor = useEditor();
const hasSelection = useEditorSelector((editor) =>
Boolean(editor.read.selection())
);
return (
<Button
disabled={!hasSelection}
onClick={() => editor.update((tx) => tx.marks.toggle('bold'))}
useEditor returns the required stable editor object. Use it for event
handlers, effects, commands, and reads that should not cause a render. It
throws when no matching editor is active.
const editor = useEditor();
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'Inserted paragraph' }],
type: 'paragraph',
});
});const editor = useEditor();
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'Inserted paragraph' }],
type: 'paragraph',
});
});useEditorSelector subscribes to a derived value. Return a primitive or provide
equalityFn when the selected value needs custom comparison.
const isSelectionExpanded = useEditorSelector(
(editor) => editor.read.selection.isExpanded()
);const isSelectionExpanded = useEditorSelector(
(editor) => editor.read.selection.isExpanded()
);useEditorState reads through the immutable state view and re-renders only
when the selected result changes.
const selection = useEditorState((state) => state.selection());
return <pre>{JSON.stringify(selection, null, 2)}</pre>;const selection = useEditorState((state) => state.selection());
return <pre>{JSON.stringify(selection, null, 2)}</pre>;Wrap shared UI in PlateController when a toolbar, side panel, or inspector
lives outside a single <Plate> tree.
import type React from 'react';
import { PlateController, useActiveEditor } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function EditorShell({ children }: { children: React.ReactNode }) {
return (
<PlateController>
<ActiveEditorToolbar />
{children}
</PlateController>
);
}
function ActiveEditorToolbar() {
const editor = useActiveEditor();
if (!editor) return null;
return (
<Button onClick={() => editor.api.dom.focus()}>Focus editor</Button>
);
}import type React from 'react';
import { PlateController, useActiveEditor } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function EditorShell({ children }: { children: React.ReactNode }) {
return (
<PlateController>
<ActiveEditorToolbar />
{children}
</PlateController>
);
}
function ActiveEditorToolbar() {
const editor = useActiveEditor();
PlateController resolves an editor by explicit id, focused editor, then
primary editors. useActiveEditor() returns null while none is active.
Components that require an editor should use useEditor() and fail fast.
Use editor.read for snapshot-bound queries, editor.api for DOM, host, and
plugin services, and editor.update for operations that change the document,
selection, history, or plugin state.
import { BoldPlugin } from '@platejs/basic-nodes/react';
const selection = editor.read.selection();
const selectedText = selection ? editor.read.text.string(selection) : '';
const currentBlock = editor.read.nodes.block();
const bold = editor.plugin(BoldPlugin);
if (selectedText && !bold.read.isActive()) {
bold.update.toggle();
}
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'New paragraph' }],
type: 'paragraph',
});
});import { BoldPlugin } from '@platejs/basic-nodes/react';
const selection = editor.read.selection();
const selectedText = selection ? editor.read.text.string(selection) : '';
const currentBlock = editor.read.nodes.block();
const bold = editor.plugin(BoldPlugin);
if (selectedText && !bold.read.isActive()) {
bold.update.toggle();
}
editor.update((tx) => {
tx.nodes.insert({
children: [{ text:
| Surface | Examples | Use for |
|---|---|---|
editor.read | text.string, nodes.block, marks, selection.isExpanded | Reading the current immutable editor state. |
editor.api | dom.focus and plugin-owned service groups | DOM/runtime services and non-mutating feature APIs. |
editor.update | tx.nodes.insert, tx.nodes.set, tx.marks.toggle, tx.selection.set | Mutating document or editor state. |
Pass a plugin descriptor to editor.plugin(plugin) to open its typed portal.
The portal exposes resolved descriptor fields and owns that plugin's scoped
API, one-shot update commands, and live state.
import { BoldPlugin } from '@platejs/basic-nodes/react';
editor.plugin(BoldPlugin).update.toggle();import { BoldPlugin } from '@platejs/basic-nodes/react';
editor.plugin(BoldPlugin).update.toggle();| Method | Use for |
|---|---|
editor.plugin(plugin) | Typed descriptor fields plus scoped API, reads, updates, and store. |
editor.plugin(plugin).name | The installed capability identity. |
editor.plugin(ElementPlugin).schema.type | The resolved persisted element type. |
editor.plugin(BoldPlugin).schema.key | The resolved persisted property key. |
editor.plugin(plugin).inject.nodeProps | Compiled injected node props for a plugin. |
Runtime-name portals are intentionally erased. Use them for dynamic names or
family-agnostic slots that accept whichever installed descriptor owns the
name. Check .installed before any other field when the named plugin is
optional; other fields throw when the plugin is absent.
Use the scoped store when imperative code needs to read or write editor-local
plugin state. Use usePluginStore when React UI needs to re-render from that
state.
import { FindReplacePlugin } from '@platejs/find-replace';
import { useEditor, usePluginStore } from 'platejs/react';
export function FindReplaceControl() {
const editor = useEditor();
const search = usePluginStore(FindReplacePlugin, 'search');
return (
<input
value={search}
onChange={(event) => {
editor
.plugin(FindReplacePlugin)
.store.set({ search: event.target.value });
editor.api.react.refreshDecorations();
}}
/>
);
}import { FindReplacePlugin } from '@platejs/find-replace';
import { useEditor, usePluginStore } from 'platejs/react';
export function FindReplaceControl() {
const editor = useEditor();
const search = usePluginStore(FindReplacePlugin, 'search');
return (
<input
value={search}
onChange={(event) => {
editor
.plugin(FindReplacePlugin)
.store.set({ search: event.target.value });
editor.api.react.refreshDecorations();
}}
const state = editor.plugin(FindReplacePlugin).store.get();
editor.plugin(FindReplacePlugin).store.set({
search: 'Plate',
});
editor.plugin(FindReplacePlugin).store.set((draft) => {
draft.search = draft.search.trim();
});const state = editor.plugin(FindReplacePlugin).store.get();
editor.plugin(FindReplacePlugin).store.set({
search: 'Plate',
});
editor.plugin(FindReplacePlugin).store.set((draft) => {
draft.search = draft.search.trim();
});| Method | Use for |
|---|---|
editor.plugin(plugin).store.get(key, ...args) | One state field or named selector result. |
editor.plugin(plugin).store.get() | Complete current plugin state. |
editor.plugin(plugin).store.set(partial) | Merge state fields. |
editor.plugin(plugin).store.set(updater) | Mutate state through a draft updater. |
| Task | Guide |
|---|---|
| Configure editor creation. | Editor Configuration |
| Add plugin APIs and transaction commands. | Plugin Methods |
| Read plugin context inside components. | Plugin Context |
| Browse editor query contracts. | Editor API |
| Browse editor transaction contracts. | Editor Transactions |