Skip to main content
Build your own toolbar UI. SuperDoc provides the state and commands — you render whatever you want.

Using React?

superdoc/headless-toolbar is the framework-agnostic substrate. For new React apps, reach for useSuperDocCommand from superdoc/ui/react instead. It re-renders one button per command (not the whole toolbar) and ships alongside companion hooks for selection, comments, and tracked changes. See Custom UI › Toolbar and commands. superdoc/headless-toolbar stays supported for existing integrations and framework-agnostic toolbar-only use cases. Both paths read from the same internal substrate.

Quick start

Don’t pass toolbar to the SuperDoc constructor. The headless toolbar replaces the built-in UI entirely — no flags needed.

Core concepts

Snapshot

Every time the editor state changes, the toolbar produces a ToolbarSnapshot. Read it to know what your UI should look like.

Execute

Run a command by ID. Returns true if the command executed, false otherwise.

Subscribe

subscribe() fires immediately with the current snapshot, then again on every change. It returns an unsubscribe function.

API reference

createHeadlessToolbar(options)

Creates a headless toolbar controller bound to a SuperDoc instance.
SuperDoc
required
The SuperDoc instance to bind to.
PublicToolbarItemId[]
Command IDs to track. When omitted, all available commands are tracked.
Returns a HeadlessToolbarController.

HeadlessToolbarController

getSnapshot()

Returns the current ToolbarSnapshot without subscribing.

subscribe(listener)

Registers a listener that receives { snapshot } on every state change. Fires immediately with the current snapshot. Returns an unsubscribe function.

execute(id, payload?)

Runs the command identified by id. Returns true if the command executed, false otherwise.

destroy()

Tears down event listeners and clears all subscriptions. Call this when unmounting your toolbar.

ToolbarSnapshot

ToolbarContext | null
The current editing context. null when no editor is active.
Partial<Record<PublicToolbarItemId, ToolbarCommandState>>
Map of command IDs to their current state.

ToolbarCommandState

boolean
Whether the command is currently active (e.g., bold is on at the cursor position).
boolean
Whether the command can run right now. Disabled when the editor isn’t editable or the command doesn’t apply to the current selection.
unknown
The current value for commands that have one (font size, text color, zoom, etc.). Not present for toggle commands like bold.

ToolbarContext

ToolbarTarget
The primary execution surface. Use target.commands for direct command access when execute() doesn’t cover your use case.
'body' | 'header' | 'footer'
Which document surface is currently active.
boolean
Whether the editor is in an editable state.
boolean
Whether the current selection is collapsed (cursor with no range).

Command reference

Snapshot values match the format you pass to execute(). What you read is what you write — no conversion needed.

Text formatting

Font controls

Paragraph

Insert

Table actions

Document

Track changes

Constants

headlessToolbarConstants provides preset option arrays for dropdown-style controls. Each option has { label, value } — use value when calling execute().

Helpers

headlessToolbarHelpers provides utilities for advanced workflows.

React example

A minimal hook that wires the headless toolbar to React state:
Then in your component:

Examples

Framework examples

Full examples with React + shadcn, React + MUI, Vue + Vuetify, Svelte, and vanilla JS