Skip to Content
API Referenceeditor-react

@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 pointContents
.StesuraEditor, EditorLoadingState, mount hooks, state and toolbar-state hooks, providers, node views, floating menus.
/typesPublic types: StesuraEditorProps, StesuraUIExtension, PluginCallback, TransactionModifierFn, NodeViews, …
/providersStesuraUserProvider, useStesuraUser, and the editor context providers.
/toolbarToolbar components. Documented in Customizing the Toolbar.
/hooksSetup and utility hooks, without the main barrel.
/dropuseEditorDrop (depends on react-dnd).
/sub-editorBuilding 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.

ArgTypeDefaultPurpose
editorIdstring"default"Editor id. Must match StesuraEditor’s field. Also registers the dispatch for getEditorDispatchById.
schemaSchemastesuraSchema()Read once at mount.
initialContentContent | Node | Fragment | JSONContentempty docRead once at mount. HTML strings are parsed with the DOM, so browser only.
configurationConfigPluginPropsForwarded to core’s configPlugin.
pluginOptionsStesuraPluginsOptionsToggles forwarded to stesuraPlugins.
extraPlugins({ editorId, parentEditorId? }) => Plugin[]Feature-pack plugins. Called for the main editor and for each sub-editor.
transactionModifierTransactionModifierFnRuns 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:

FieldPurpose
schemaThe schema, resolved once per mount.
pluginFactoryStable 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.
setStateRefAssign 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.

PropTypeDefaultPurpose
state (required)EditorState | nullnull while the mount initialises; the toolbar and a skeleton render meanwhile.
dispatchTransaction (required)(tr) => voidThe mount hook’s dispatch. If it throws, the edit is dropped and an error toast shown.
schema (required)SchemaMust match the schema state was created with.
pluginFactory (required)PluginCallbackBuilds plugin sets for sub-editors. Read once at mount; pass the mount hook’s.
fieldstring"default"Editor id. Must match the mount hook’s editorId. Read once: to switch documents, remount with a key.
transactionModifierTransactionModifierFnModifier 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.
editablebooleantrueDeprecated. editable={false} maps to mode="view". mode wins when both are set.
toolbarReactNode | falsebuilt-inA custom toolbar, or false for none. The built-in toolbar is lazy-loaded, so replacing it skips its chunk.
printModebooleanfalseChrome-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) => voidFires when a document surface (main or sub-editor) receives focus.
nodeViewsNodeViewsNode views by node type name. Override built-in and extension views. Must be a stable reference.
uiExtensionsStesuraUIExtension[]Feature-pack UI: panels, toolbar groups, menus, node views. Must be a stable reference.
TabsReactNodeTab bar above the document.
DocumentBanner({ field }) => ReactNodeBanner at the top-left of the canvas.
userPreferencesPartial<UserPreferences>Preference defaults, applied once on first mount. Keys already in localStorage are kept.
localeSupportedLocale"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.

PropTypePurpose
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();
HookReturns
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, …

HookPurpose
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.

ExportPurpose
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

HookEntry pointsPurpose
useT()root, /hooksTranslation map for the active locale. Re-renders on locale change.
useLocale()root, /hooksActive locale code.
useApplyLocale(locale)/hooksLoads 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.

ExportPurpose
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’s dispatchBuild, or null. 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.
Last updated on