Plan with query.match, then apply with mutations
This is the recommended default for most apps: match first, preview, then apply.Run multiple edits as one plan
When several changes should stay together, group them into one plan:Quick search and single edit
For lightweight text edits, usequery.match and apply against the canonical selection target returned by the match:
Find text and insert at position
Search for a heading (or any text) and insert a new paragraph relative to it:address from query.match is a BlockNodeAddress that works directly with create.paragraph, create.heading, and create.table. Use kind: 'before' to insert before the matched node instead.
To insert as a tracked change, pass changeMode: 'tracked':
Use
query.match (not find) for this workflow. query.match returns BlockNodeAddress objects that are directly compatible with mutation targets.item.target. For plans or multi-step edits, prefer item.handle.ref so every step reuses the same resolved match.
Chain table mutations with returned refs
For non-destructive table-targeted mutations, reuseresult.table.nodeId from the previous success result. You do not need an intermediate find() between calls.
This handoff contract applies to table-targeted calls. Cell-targeted
tables.setBorder, tables.clearBorder, tables.setShading, and tables.clearShading still return the targeted tableCell address today.Build a selection explicitly with ranges.resolve
Useranges.resolve when you already know the anchor points and want a transparent SelectionTarget plus a reusable mutation-ready ref:
Tracked-mode insert
Insert text as a tracked change so reviewers can accept or reject it:resolution with the resolved insertion point and inserted entries with tracked-change IDs.
Check capabilities before acting
Usecapabilities() to branch on what the editor supports:
Cross-session block addressing
When you load a DOCX, close the editor, and load the same file again,sdBlockId values change: they’re regenerated on every open. For cross-session block targeting, use query.match addresses (NodeAddress with kind: 'block'), which carry DOCX-native paraId-derived IDs when available.
This pattern is common in headless pipelines: extract block references in one session, then apply edits in another.
Navigate to saved addresses in the browser
When the saved addresses are used in a browser-based viewer (RAG citations, search results, cross-references), pass thenodeId directly to scrollToElement:
scrollToElement also works with comment and tracked change IDs:
nodeId stability depends on the ID source. For DOCX-imported content, nodeId comes from paraId when available and is best-effort stable across loads. Runtime-created content is still not guaranteed stable across loads; many nodes use session-scoped editor identity, while some structures such as tables or table cells may expose deterministic fallback IDs instead of raw sdBlockId.Content extraction for RAG
doc.extract() returns all document content in one call: blocks with full text, comments, and tracked changes. Each item has a stable ID that works directly with scrollToElement.
RAG pipeline pattern
Extract content, chunk it, store the IDs, and navigate back on click:All IDs from
doc.extract() work directly with scrollToElement(): no conversion needed. For DOCX-imported content, block nodeId values are stable across sessions.Read document counts
doc.info() returns a snapshot of current document statistics including word, character, paragraph, heading, table, image, comment, tracked-change, SDT-field, and list counts.
Build a live counter in the browser
doc.info() is a snapshot read. To build a live counter, subscribe to document-change events and refresh counts in the handler: do not poll in a render loop.
SuperEditor (raw editor):
SDK usage
The SDKs do not expose browser event subscriptions. Calldoc.info() at workflow boundaries: after opening a document, after a batch of mutations, or before saving.
Dry-run preview
PassdryRun: true to validate an operation without applying it:

