Skip to main content
Document API gives you a consistent way to read and edit documents without relying on editor internals.

Why use Document API

  • Build automations without editor-specific code.
  • Work with predictable inputs and outputs defined per operation.
  • Check capabilities up front and branch safely when features are unavailable.

Node addressing

Every block in a document has a nodeId — a string that uniquely identifies it. For mutation targeting and getNode(...), use NodeAddress:
{
  "kind": "block",
  "nodeType": "paragraph",
  "nodeId": "3A2B1C0D"
}
find(...) returns NodeAddress values (for example kind: "block"). For text selectors, query.match(...) returns deterministic mutation-ready data: item.target as a canonical SelectionTarget, item.handle.ref as a reusable resolved handle, and item.address as the matching NodeAddress.

Mutation targeting

Core selection mutations such as replace, delete, format.*, and selection-based mutation plans use SelectionTarget or a mutation-ready ref.
{
  "kind": "selection",
  "start": { "kind": "text", "blockId": "p1", "offset": 0 },
  "end": { "kind": "text", "blockId": "p1", "offset": 5 }
}
Use item.target from query.match(...) for single direct operations:
const match = editor.doc.query.match({
  select: { type: 'text', pattern: 'ACME Corp' },
  require: 'first',
});

const target = match.items?.[0]?.target;
if (target) {
  editor.doc.replace({ target, text: 'NewCo Inc.' });
}
Use item.handle.ref when you want multiple operations or a mutation plan to reuse the same resolved range. When you need to construct a selection from explicit anchors, call editor.doc.ranges.resolve(...) and use its target or handle.ref.
SelectionTarget is the canonical target for core selection mutations. Other APIs, such as comments or insertion-point operations, may still use TextAddress where the operation is defined around a specific text span or insertion point.

Stable IDs across loads

For DOCX documents, nodeId is derived from the file’s native w14:paraId attribute. In practice, this is usually stable when you reopen the same unchanged DOCX across separate editor sessions, machines, or headless CLI pipelines. For nodes created at runtime (not imported from DOCX), nodeId is still best-effort only. Many runtime nodes use session-scoped editor identity, and some structures such as tables or table cells may expose deterministic fallback IDs instead of a raw UUID-like sdBlockId. Either way, cross-session stability is not guaranteed.
ID sourceStable across loads?When used
paraId (from DOCX)Best effort (usually stable for unchanged DOCX blocks)Paragraphs and table rows imported from DOCX
Runtime-derived IDNo guarantee (often session-scoped; some table addresses use deterministic fallbacks)Nodes created programmatically before first export
If you need to reference blocks across separate editor sessions, use editor.doc.query.match() (or persist nodeId and reconstruct a NodeAddress) — don’t read node.attrs.sdBlockId directly. The Document API resolves paraId first for DOCX-imported content and may derive safer public IDs for some runtime structures such as tables.
No block ID is guaranteed to survive all Microsoft Word round-trips or external document rewrites. Word and other tools may regenerate w14:paraId during structural changes (for example split/merge/rebuild operations), and SuperDoc may rewrite duplicate IDs on import to keep block targeting deterministic.