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.
Access the Editor
| Need | Use |
|---|---|
| Read the editor inside callbacks without re-rendering. | useEditorRef() |
| Re-render from one derived value. | useEditorSelector(selector, deps) |
| Re-render on every editor change. | useEditorState() |
Reach the active editor from outside a <Plate> subtree. | <PlateController> plus the same hooks. |
import { useEditorRef, useEditorSelector } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function BoldButton() {
const editor = useEditorRef();
const hasSelection = useEditorSelector(
(editor) => Boolean(editor.selection),
[]
);
return (
<Button
disabled={!hasSelection}
onClick={() => editor.update((tx) => tx.marks.toggle('bold'))}
>
Bold
</Button>
);
}import { useEditorRef, useEditorSelector } from 'platejs/react';
import { Button } from '@/components/ui/button';
export function BoldButton() {
const editor = useEditorRef();
const hasSelection = useEditorSelector(
(editor) => Boolean(editor.selection),
[]
);
return (
<Button
disabled={!hasSelection}
onClick={() => editor.update((tx) => tx.marks.toggle('bold'))}
>
Bold
</Button>
);
}useEditorRef
useEditorRef returns the stable editor object. Use it for event handlers,
effects, commands, and reads that should not cause a render.
const editor = useEditorRef();
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'Inserted paragraph' }],
type: 'p',
});
});const editor = useEditorRef();
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'Inserted paragraph' }],
type: 'p',
});
});useEditorSelector
useEditorSelector subscribes to a derived value. Return a primitive or provide
equalityFn when the selected value needs custom comparison.
const isSelectionExpanded = useEditorSelector(
(editor) => editor.api.isExpanded(),
[]
);const isSelectionExpanded = useEditorSelector(
(editor) => editor.api.isExpanded(),
[]
);useEditorState
useEditorState subscribes to the whole editor state. Use it only when the UI
really needs to update on every editor change.
const editor = useEditorState();
return <pre>{JSON.stringify(editor.selection, null, 2)}</pre>;const editor = useEditorState();
return <pre>{JSON.stringify(editor.selection, null, 2)}</pre>;Outside Plate
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, useEditorMounted, useEditorRef } 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 = useEditorRef();
const mounted = useEditorMounted();
if (!mounted || editor.runtime.isFallback) return null;
return (
<Button onClick={() => editor.api.dom.focus()}>Focus editor</Button>
);
}import type React from 'react';
import { PlateController, useEditorMounted, useEditorRef } 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 = useEditorRef();
const mounted = useEditorMounted();
if (!mounted || editor.runtime.isFallback) return null;
return (
<Button onClick={() => editor.api.dom.focus()}>Focus editor</Button>
);
}PlateController resolves an editor by explicit id, focused editor, then
primary editors. If a controller exists but no editor store is ready, hooks
return a fallback editor; check useEditorMounted() or
!editor.runtime.isFallback before running commands.
Editor API and Commands
Use editor.api for queries, DOM services, and host helpers. Use
editor.update for operations that change the document, selection, history, or
plugin state.
const selectedText = editor.selection
? editor.api.string(editor.selection)
: '';
const currentBlock = editor.api.block();
if (selectedText && !editor.api.hasMark('bold')) {
editor.update((tx) => tx.marks.toggle('bold'));
}
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'New paragraph' }],
type: 'p',
});
});const selectedText = editor.selection
? editor.api.string(editor.selection)
: '';
const currentBlock = editor.api.block();
if (selectedText && !editor.api.hasMark('bold')) {
editor.update((tx) => tx.marks.toggle('bold'));
}
editor.update((tx) => {
tx.nodes.insert({
children: [{ text: 'New paragraph' }],
type: 'p',
});
});| Surface | Examples | Use for |
|---|---|---|
editor.api | string, block, hasMark, isExpanded, dom.focus | Reading editor state, checking selection, DOM/runtime services. |
editor.update | tx.nodes.insert, tx.nodes.set, tx.marks.toggle, tx.selection.set | Mutating document or editor state. |
Plugin Methods
Plugin helpers let TypeScript infer APIs from the plugin you pass in. Plugin
commands run through editor.update.
import { TablePlugin } from '@platejs/table/react';
const cell = editor.api.create.tableCell();
editor.update((tx) => tx.insert.tableRow());import { TablePlugin } from '@platejs/table/react';
const cell = editor.api.create.tableCell();
editor.update((tx) => tx.insert.tableRow());| Method | Use for |
|---|---|
editor.getPlugin(plugin) | The resolved plugin instance for a key or plugin object. |
editor.getType(pluginKey) | The node type registered for a plugin key. |
editor.getInjectProps(plugin) | Resolved injected node props for a plugin. |
Plugin Options
Use editor option methods when imperative code needs to read or write plugin
configuration. Use usePluginOption or usePluginOptions when React UI needs
to re-render from option state.
import { FindReplacePlugin } from '@platejs/find-replace';
import { useEditorRef, usePluginOption } from 'platejs/react';
export function FindReplaceControl() {
const editor = useEditorRef();
const search = usePluginOption(FindReplacePlugin, 'search');
return (
<input
value={search}
onChange={(event) => {
editor.plugin(FindReplacePlugin).setOption('search', event.target.value);
}}
/>
);
}import { FindReplacePlugin } from '@platejs/find-replace';
import { useEditorRef, usePluginOption } from 'platejs/react';
export function FindReplaceControl() {
const editor = useEditorRef();
const search = usePluginOption(FindReplacePlugin, 'search');
return (
<input
value={search}
onChange={(event) => {
editor.plugin(FindReplacePlugin).setOption('search', event.target.value);
}}
/>
);
}const options = editor.plugin(FindReplacePlugin).getOptions();
editor.plugin(FindReplacePlugin).setOptions({
search: 'Plate',
});
editor.plugin(FindReplacePlugin).setOptions((draft) => {
draft.search = draft.search.trim();
});const options = editor.plugin(FindReplacePlugin).getOptions();
editor.plugin(FindReplacePlugin).setOptions({
search: 'Plate',
});
editor.plugin(FindReplacePlugin).setOptions((draft) => {
draft.search = draft.search.trim();
});| Method | Use for |
|---|---|
editor.plugin(plugin).getOption(key, ...args) | One option or selector value. |
editor.plugin(plugin).getOptions() | Current option state for a plugin. |
editor.plugin(plugin).setOption(key, value) | One option update. |
editor.plugin(plugin).setOptions(partial) | Merge multiple option fields. |
editor.plugin(plugin).setOptions(updater) | Mutate option state through a draft updater. |
editor.getOptionsStore(plugin) | Low-level access to the plugin options store. |
Next Steps
| 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 |