@stesura/editor-react
The editor shell: the StesuraEditor component, the mount hooks that produce
its state, and the hooks the editor UI is built from.
| Entry point | Contents |
|---|---|
. | StesuraEditor, EditorLoadingState, mount hooks, state and toolbar-state hooks, providers, node views, floating menus. |
/types | Public types: StesuraEditorProps, StesuraUIExtension, PluginCallback, TransactionModifierFn, NodeViews, … |
/providers | StesuraUserProvider, useStesuraUser, and the editor context providers. |
/toolbar | Toolbar components. Documented in Customizing the Toolbar. |
/hooks | Setup and utility hooks, without the main barrel. |
/drop | useEditorDrop (depends on react-dnd). |
/sub-editor | Building blocks for nested editors. |
Guides: Getting Started for mounting, Customizing the Editor for the toolbar, menu and extension APIs.
Mount hooks
Mount hooks read currentUser from StesuraUserProvider and throw outside it.
useLocalEditor(args?)
In-memory editor state, no backend. Returns
{ editorState, dispatch, pluginFactory, schema }, the same shape as the
collab mount hooks, so backends are interchangeable. Exported from the root and
/hooks.
| Arg | Type | Default | Purpose |
|---|---|---|---|
editorId | string | "default" | Editor id. Must match StesuraEditor’s field. Also registers the dispatch for getEditorDispatchById. |
schema | Schema | stesuraSchema() | Read once at mount. |
initialContent | Content | Node | Fragment | JSONContent | empty doc | Read once at mount. HTML strings are parsed with the DOM, so browser only. |
configuration | ConfigPluginProps | Forwarded to core’s configPlugin. | |
pluginOptions | StesuraPluginsOptions | Toggles forwarded to stesuraPlugins. | |
extraPlugins | ({ editorId, parentEditorId? }) => Plugin[] | Feature-pack plugins. Called for the main editor and for each sub-editor. | |
transactionModifier | TransactionModifierFn | Runs on every dispatch (e.g. trackChangesTransactionModifier). The latest value is used. |
The main state’s plugins are built once. Later changes to configuration,
pluginOptions or extraPlugins only reach sub-editors created afterwards.
On mount the hook also loads the editor fonts (loadEditorFonts) and dispatches
one normalizing transaction, which heals out-of-range attributes and runs the
derived passes (numbering, cross-references) on the fresh document.
useEditorCore(args)
The base hook behind every mount backend (useLocalEditor, useCollabEditor,
useLocalCollabEditor). Takes editorId, schema, configuration,
pluginOptions, extraPlugins and transactionModifier, as above, and returns:
| Field | Purpose |
|---|---|
schema | The schema, resolved once per mount. |
pluginFactory | Stable PluginCallback. |
dispatch | (tr) => void. |
dispatchBuild | (build: (state) => Transaction | null | undefined) => void. Runs build inside the state updater, against the state the transaction is applied to. build must be pure: StrictMode calls it twice. |
setStateRef | Assign your backend’s setState to setStateRef.current in a layout effect. |
Every dispatch drops local document changes in "view" mode, runs the
attr-bounds guard, then calls
transactionModifier(state, tr, currentUser, forceTrackChanges).
forceTrackChanges is true for local transactions in "review" mode.
dispatch and dispatchBuild change identity when currentUser changes.
useStesuraPluginFactory(args)
Just the plugin factory, for hosts that own their EditorState. Takes
{ schema, configuration?, pluginOptions?, extraPlugins? } and returns a
permanently stable PluginCallback that builds the core plugins plus
extraPlugins. Inputs are read on each call. On the root and /hooks.
StesuraEditor props
Type: StesuraEditorProps from @stesura/editor-react/types.
| Prop | Type | Default | Purpose |
|---|---|---|---|
state (required) | EditorState | null | null while the mount initialises; the toolbar and a skeleton render meanwhile. | |
dispatchTransaction (required) | (tr) => void | The mount hook’s dispatch. If it throws, the edit is dropped and an error toast shown. | |
schema (required) | Schema | Must match the schema state was created with. | |
pluginFactory (required) | PluginCallback | Builds plugin sets for sub-editors. Read once at mount; pass the mount hook’s. | |
field | string | "default" | Editor id. Must match the mount hook’s editorId. Read once: to switch documents, remount with a key. |
transactionModifier | TransactionModifierFn | Modifier for sub-editors (headers/footers, notes), read once at mount. The main editor’s modifier goes to the mount hook, so pass the same one to both. | |
mode | "edit" | "review" | "view" | "edit" | "review": every local edit becomes a tracked suggestion, whatever the user’s toggle; throws if the track-changes plugin is not mounted. "view": read-only; local document changes are dropped at dispatch, remote collab changes still apply. See Editor modes. |
editable | boolean | true | Deprecated. editable={false} maps to mode="view". mode wins when both are set. |
toolbar | ReactNode | false | built-in | A custom toolbar, or false for none. The built-in toolbar is lazy-loaded, so replacing it skips its chunk. |
printMode | boolean | false | Chrome-less render for PDF/print capture: no toolbar, panels, zoom or scroll area. Also on when the state’s printMount config is set. |
onFileUpload | (file, type, contentType?) => Promise<string | undefined> | Uploads a file and resolves to the URL to embed. Used by upload blocks and for images in imported DOCX files. contentType is sniffed from the file’s magic bytes: store with it, not with file.type, which the client controls. See File & Image Uploads. | |
onContentFocus | (editorId, event) => void | Fires when a document surface (main or sub-editor) receives focus. | |
nodeViews | NodeViews | Node views by node type name. Override built-in and extension views. Must be a stable reference. | |
uiExtensions | StesuraUIExtension[] | Feature-pack UI: panels, toolbar groups, menus, node views. Must be a stable reference. | |
Tabs | ReactNode | Tab bar above the document. | |
DocumentBanner | ({ field }) => ReactNode | Banner at the top-left of the canvas. | |
userPreferences | Partial<UserPreferences> | Preference defaults, applied once on first mount. Keys already in localStorage are kept. | |
locale | SupportedLocale | "en" | UI locale. Other locales load as separate chunks. |
iconSet | "fluent" | "classic" | "fluent" | Icon set. |
className and contextMenu are declared on StesuraEditorProps but not read.
Don’t rely on them.
EditorLoadingState renders the editor’s loading skeleton and is safe to
import statically, e.g. as the loading fallback of a dynamic() import. Pass
it the same toolbar value as the editor: with the default toolbar it also
prefetches the toolbar chunk.
StesuraUserProvider
Required above StesuraEditor and the mount hooks. Exported from the root and
/providers; useStesuraUser() reads it.
| Prop | Type | Purpose |
|---|---|---|
currentUser | { id, name?, avatar?, color? } | Author of comments and tracked changes, collab presence identity. |
resolveUsers | ({ userIds }) => Promise<{ name, avatar? }[]> | Resolves ids to display data. Results are index-aligned with userIds. |
resolveMentionSuggestions | ({ text }) => Promise<string[]> | User ids matching an @ mention query. Without it the mention dropdown never opens. |
Panel controls
Programmatic open/close for the three panel areas that uiExtensions populate:
import { useLeftPanelControls } from "@stesura/editor-react";
const { isLeftPanelOpen, leftPanelAction, openLeftPanel, closeLeftPanel,
toggleLeftPanel, setLeftPanelAction } = useLeftPanelControls();| Hook | Returns |
|---|---|
useLeftPanelControls() | { isLeftPanelOpen, leftPanelAction, openLeftPanel(action), closeLeftPanel(), toggleLeftPanel(action), setLeftPanelAction(action) } |
useRightPanelControls() | The same with RightPanel names, where openRightPanel(action, meta?) and closeRightPanel({ skipGuard? }), plus the unsaved-changes guard: guard, setGuard(fn), resetGuard(), checkGuard, pendingAction, executePendingAction(), cancelPendingAction(). |
useBottomPanelControls() | The same with BottomPanel names. |
action is the key a panel registered under (leftPanels[].action, …).
Toggling with a different action switches panels instead of closing.
Each hook is also split in two: use…PanelActions() returns only the actions,
is stable and doesn’t subscribe (use it when you only open or close), and
use…PanelState() returns only the state. Panel state is shared by every
editor on the page.
A right-panel guard is () => boolean. When it returns false, the open,
close, toggle or action change is stored as pendingAction instead;
executePendingAction() replays it once the user confirms.
User preferences
Global, persisted to localStorage and shared by every editor on the page.
Keys are those of UserPreferences (@stesura/core/types): showOutline,
showRuler, zoomLevel, trackChanges, showComments, displayUnits,
proofReadEnabled, …
| Hook | Purpose |
|---|---|
useUserPreferenceValue(key) | Subscribes to one preference. |
useSetUserPreference(key) | Stable setter, no subscription. Takes a value or an updater function. |
useUserPreference(key) | [value, setter]. |
useShowOutline() | Whether table outlines render in the surrounding editor: the showOutline preference, forced on in header/footer editors, always off in print mode. |
Search navigation
Lets a search match inside a hidden region (footnote and endnote bodies, headers and footers, comments) open the panel or sub-editor that displays it.
| Export | Purpose |
|---|---|
usePublishSearchNavigation() | Returns a publisher. Call it with the editor state right after a find/replace command moved the selection to a match, or with null when search closes. |
useSearchNavigationTarget() | The current SearchNavigationTarget ({ region, from, to, nav }) or null. from/to are main-document positions; nav increments on every navigation. |
useSearchRegionListener(config) | Drives a panel from navigation. config is { match(region), onEnter(key, target), onLeave() }: match returns a stable key for regions the panel owns, else null. onEnter fires on every navigation inside the region, so it must be idempotent. Mount it next to the panel it drives. |
i18n
| Hook | Entry points | Purpose |
|---|---|---|
useT() | root, /hooks | Translation map for the active locale. Re-renders on locale change. |
useLocale() | root, /hooks | Active locale code. |
useApplyLocale(locale) | /hooks | Loads and applies a locale for the whole page, falling back to English if loading fails. StesuraEditor already calls it with its locale prop. |
/hooks subpath
Setup and utility hooks, importable without the main barrel: useLocalEditor,
useEditorCore, useStesuraPluginFactory, useT, useLocale,
useApplyLocale, useDirection, useEditorStateSelectorEq,
useIsCompactLayout, useReportNodeResize, useAutomaticColorPickerLabels,
usePdfPreviewCapability, usePdfPreviewConfig, preserveNodeViewKey,
toastText. Types: UseLocalEditorArgs, UseEditorCoreArgs.
/drop subpath
Drops from external react-dnd sources onto the canvas. A separate entry point
so react-dnd stays out of bundles that don’t use it. The editor does not
render a DndProvider; put one above it.
| Export | Purpose |
|---|---|
useEditorDrop({ view, field, accept, onDropCallback, canDropCallback? }) | Returns the drop ref to attach. accept lists react-dnd item types. onDropCallback({ view, pos, item, field }) returns { dropped: true } when it handled the drop; it isn’t called in a read-only view or after a nested target handled the drop. canDropCallback takes the same argument; omitted means every drop is valid. |
dropPositionFromMonitor(view, monitor) | Document position under the drag pointer, or null. 0 is a valid position. |
/sub-editor subpath
Building blocks for nested editors (header/footer, footnote and endnote
panels). A sub-editor mirrors one region of the main document in both
directions and builds its own plugin instances from your pluginFactory. The
built-in panels use it; you only need it for a custom panel over a document
region. Exports: useSubEditor, useSubEditorSchema, useSubEditorPlugins,
useSubEditorNodeViews, useSyncSearchMatch, SubEditorRegister,
FocusOnOpen, FocusOnRequest.
Also on the root barrel
- Toolbar state hooks (
useRunTbCommand, probes, selectors, bound-editor hooks): Customizing the Toolbar. - Floating-menu hooks and components: Customizing the Menus.
- Node-view helpers (
useStesuraNodeView,useNodeViewSlots,stesuraNodeViews): Node Views. - Mode hooks (
useSetEditorMode,useEditorStoreValue): Editor modes. - Per-editor registries for multi-editor hosts:
useEditorStateById,useEditorViewById,useCurrentRootEditorId,useHeaderFooterEditing. getEditorDispatchById(id): the mounted editor’sdispatchBuild, ornull. For dispatching outside event handlers:getEditorDispatchById("default")?.((state) => state.tr.insertText("Hi")).SearchBar,ProseMirrorDoc, and the layout pieces (EditorResizableLayout,EditorLeftPanel,EditorRightPanel,PanelLayout) for custom shells.