Replace and delete content
Use fresh query targets to change an open DOCX and inspect each mutation receipt.
Use replace() and delete() after a query has identified the exact content to change. Both operations accept a target from query.match() and return a receipt that says whether the mutation succeeded.
For HTML, Markdown, or SDFragment replacement, see insert HTML and Markdown. Rich replacement uses the same fresh-target and revision-guard rules described here.
To replace every block in the main body, pass the explicit body target. This is a Document API content mutation: it keeps the open DOCX package and its terminal page setup while replacing the body blocks in one undo step.
const body = { kind: 'story', storyType: 'body' } as const;
const receipt = await doc.replace(
{ target: body, type: 'markdown', value: '# Replacement\n\nNew body.' },
{ changeMode: 'direct', expectedRevision },
);The body target also accepts text or a preconverted content fragment. It is main-body-only and direct-mode-only. Tracked whole-body replacement fails with CAPABILITY_UNSUPPORTED; use a selection or block target when the replacement must remain a review suggestion. To open a different DOCX package, use the editor's file replacement flow instead.
Paragraph alternatives with exact review restoration
Use the existing selection.extractOoxml() and replace() operations for paragraph alternatives. On a non-collaborative v2 session, replaceMode: 'contents' replaces one direct body paragraph's inlines while keeping its stable block identity and exact paragraph properties, including numbering. changeMode: 'direct' bypasses Suggesting for this call without changing the editor's mode. Every actual choice is one native Undo entry; an identical restore creates none.
Retain the original before applying an alternate:
const at = {
kind: 'selection',
start: { kind: 'nodeEdge', node: target, edge: 'before' },
end: { kind: 'nodeEdge', node: target, edge: 'after' },
} as const;
const original = await doc.selection.extractOoxml({ at, retain: true });
const replaced = await doc.replace(
{ target, type: 'markdown', value: 'Alternate **clause**.' },
{ replaceMode: 'contents', expectedSnapshotId: original.snapshotId, changeMode: 'direct' },
);
if (!replaced.success) throw new Error(replaced.failure.message);
const current = await doc.selection.extractOoxml({ at, retain: true });
const restored = await doc.replace(
{ target, snapshotId: original.snapshotId },
{ replaceMode: 'contents', expectedSnapshotId: current.snapshotId, changeMode: 'direct' },
);
if (!restored.success) throw new Error(restored.failure.message);
await doc.selection.releaseSnapshot({ snapshotId: current.snapshotId });
await doc.selection.releaseSnapshot({ snapshotId: original.snapshotId });The capture remains a portable Word XML manifest. Its optional snapshotId is a separate, session-owned exact restoration handle. Restoration retains original revision wrappers, local comment anchors, complete comment threads and metadata. Passing a portable capture instead copies content and remints identities; it does not restore the original.
expectedSnapshotId compares the live paragraph and its owned review data with a retained unchanged choice, so unrelated paragraph and comment edits can survive restoration. Snapshot restoration requires this guard. expectedRevision adds an optional global check. Repeated captures may reuse an opaque handle; each read returns fresh provenance. Retained captures expose source.packageRevision for pairing with a committed review window. It is an opaque package coordinate, distinct from the public evaluatedRevision; do not parse or interchange them.
Contents replacement accepts plain text, one HTML/Markdown or canonical SDFragment paragraph, and supported native OOXML. Text cannot contain tabs, line breaks or controls. A rich alternative cannot change the retained container's paragraph properties or list identity. Native paragraph properties may be omitted or match the retained properties exactly. Conflicting properties, multiple paragraphs, fields, content controls, moves, structural/formatting revisions and cross-paragraph comments fail before mutation. Ordinary hyperlinks and image drawings are supported with dependency guards. This route is direct-mode-only, supports dryRun, and currently requires a direct body paragraph (including a heading or list item) in a non-collaborative session.
Handles expire on whole-file replacement or disposal, remain usable through Undo/Redo, and are limited to 64 unique snapshots and 4 MiB per session. Release unused handles; an identical current capture can reuse the original's handle, so keep shared handles until no choice needs them. Edited local content, changed dependency definitions, missing metadata or identity collisions reject restoration rather than overwriting new work. After Undo/Redo, take a fresh scoped capture to reconcile the selected radio choice.
This guide uses the tracked-changes fixture. Serve it from /contract.docx in your app, or update the document URL in the example.
1. Run two targeted mutations
Wait for the editor, replace one company name, then run a fresh query before deleting a sentence:
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';
const superdoc = new SuperDoc({
selector: '#editor',
document: '/contract.docx',
onReady: async ({ superdoc }) => {
const doc = superdoc.activeEditor?.doc;
if (!doc) throw new Error('The active document is unavailable.');
const companyMatch = await doc.query.match({
select: { type: 'text', pattern: 'Amazing' },
require: 'exactlyOne',
});
const company = companyMatch.items[0];
if (!company || company.matchKind !== 'text') {
throw new Error('The company name was not found.');
}
const replaceReceipt = await doc.replace(
{
target: company.target,
text: 'Northstar',
},
{
changeMode: 'direct',
expectedRevision: companyMatch.evaluatedRevision,
},
);
if (!replaceReceipt.success) {
throw new Error(`Replace failed: ${replaceReceipt.failure?.message ?? 'Unknown error'}`);
}
console.log('Replace receipt:', replaceReceipt);
const liabilityMatch = await doc.query.match({
select: {
type: 'text',
pattern: 'The total liability under this section shall not exceed $500,000.',
},
require: 'exactlyOne',
});
const liability = liabilityMatch.items[0];
if (!liability || liability.matchKind !== 'text') {
throw new Error('The liability sentence was not found.');
}
const deleteReceipt = await doc.delete(
{
target: liability.target,
behavior: 'exact',
},
{
changeMode: 'direct',
expectedRevision: liabilityMatch.evaluatedRevision,
},
);
if (!deleteReceipt.success) {
throw new Error(`Delete failed: ${deleteReceipt.failure.message}`);
}
console.log('Delete receipt:', deleteReceipt);
},
});
window.addEventListener('beforeunload', () => {
superdoc.destroy();
});
The displayed source is typechecked against the public v2 browser API.
2. Re-query after a mutation
The replacement advances the document revision. The example runs a new query before deleting instead of reusing an earlier target.
expectedRevision makes each operation reject stale query input. changeMode: 'direct' applies the change immediately. Use 'tracked' when a change should remain a suggestion for human review.
For an explicit single-paragraph text selection, expectedText checks the selected text before a plain-text replacement. A mismatch returns PRECONDITION_FAILED without changing the document. This guard supports empty strings, but does not support search refs, complete-body targets, or selections across paragraphs.
When building a target from projectHtml({ includeSourceMap: true }), retain the source map's coordinateSpace on the selection and pass the projection's evaluatedRevision as expectedRevision. Source-map offsets can include deleted revision text; dropping the coordinate metadata changes what those numbers select. Add expectedText with the intended token, then obtain a fresh projection after each edit.
behavior: 'exact' removes only the resolved text range. The default 'selection' behavior may expand to block edges when the query covers an entire boundary block.
3. Inspect receipts before continuing
Check success before saving, exporting, or starting another dependent operation. A failed receipt includes a stable failure code and message. A successful receipt includes the resolved target, and may include revision and effect details depending on the operation.
Common failures at this stage mean the target is stale, the text no longer matches, or the current editor mode does not allow the mutation. Re-query current document state before retrying. Do not guess offsets or silently ignore the receipt.
Next, learn how to handle receipts and errors without unsafe retry loops. For several edits that must succeed together, use a revision-guarded mutation plan instead of chaining independent calls. The generated replace reference and delete reference list the full input and receipt shapes.