# Preview and apply mutation plans

> Validate several document edits, then apply them together against one document revision.





Use a mutation plan when several edits belong to one logical change. A plan resolves every target, previews the combined work without changing the document, and applies valid steps as one atomic transaction.

For one independent replacement or deletion, prefer the direct operations in [Replace and delete content](/document-api/replace-delete-content).

> **Diagram:** Two query references become one atomic plan that is previewed before all steps apply together.


## Run a complete browser example [#run-a-complete-browser-example]

Create a browser app with an `#editor` mount element and an explicit container height, as shown in the [Editor quickstart](/editor/quickstart). This example expects the [tracked-changes fixture](/fixtures/tracked-changes.docx) at `/contract.docx`. Its source lives in the docs app and is typechecked against the public v2 browser API:

```ts
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 [companyResult, liabilityResult] = await Promise.all([
      doc.query.match({
        select: { type: 'text', pattern: 'Amazing' },
        require: 'exactlyOne',
      }),
      doc.query.match({
        select: { type: 'text', pattern: '$500,000' },
        require: 'exactlyOne',
      }),
    ]);

    const company = companyResult.items[0];
    const liability = liabilityResult.items[0];

    if (!company || company.matchKind !== 'text' || !liability || liability.matchKind !== 'text') {
      throw new Error('The expected contract text was not found.');
    }

    if (companyResult.evaluatedRevision !== liabilityResult.evaluatedRevision) {
      throw new Error('The document changed while the plan targets were being collected.');
    }

    const plan = {
      expectedRevision: companyResult.evaluatedRevision,
      atomic: true as const,
      changeMode: 'tracked' as const,
      steps: [
        {
          id: 'rename-company',
          op: 'text.rewrite' as const,
          where: { by: 'ref' as const, ref: company.handle.ref },
          args: { replacement: { text: 'Northstar' } },
        },
        {
          id: 'lower-liability-cap',
          op: 'text.rewrite' as const,
          where: { by: 'ref' as const, ref: liability.handle.ref },
          args: { replacement: { text: '$250,000' } },
        },
      ],
    };

    const preview = await doc.mutations.preview(plan);
    if (!preview.valid) {
      throw new Error(preview.failures?.map((failure) => failure.message).join('; ') ?? 'Plan preview failed.');
    }

    const receipt = await doc.mutations.apply(plan);
    console.log('Applied steps:', receipt.steps);
  },
});

window.addEventListener('beforeunload', () => {
  superdoc.destroy();
});

```

The example queries both targets before applying any change. It also confirms that both matches came from the same document revision before building the plan.

## Build the plan from references [#build-the-plan-from-references]

Use `item.handle.ref` for plan steps. Each ref points to content resolved by `query.match()`, and `expectedRevision` binds the plan to the document state that produced those refs.

Every plan requires:

* `atomic: true`
* `changeMode: 'direct'` or `'tracked'`
* A stable, unique `id` for every step
* A supported step `op`
* A `where` target and operation-specific `args`

Check `doc.capabilities().planEngine.supportedStepOps` before constructing plans dynamically. The generated reference remains authoritative for each step shape.

## Preview without changing the document [#preview-without-changing-the-document]

`doc.mutations.preview(plan)` resolves targets and validates every step without applying document changes.

Read:

* `valid` before calling `apply()`
* `failures` for the step ID, phase, code, and message
* `steps` for the targets each step resolved
* `evaluatedRevision` for the state used during preview

A valid preview is still a snapshot. Another writer can change the document before apply, so keep `expectedRevision` on the plan.

## Apply atomically [#apply-atomically]

`doc.mutations.apply(plan)` commits every step together. If compilation, target resolution, an assertion, or revision validation fails, the plan does not partially apply successful steps.

The returned plan receipt includes the before/after revision, a result for every step, any tracked-change addresses, and timing metadata. Use step results for verification and diagnostics, not for inventing performance claims.

> **Verification target (success)**
>
> The preview should report `valid: true`. Apply should return two changed steps, and the Editor should show `Northstar
>   Corp` plus a `$250,000` liability cap as tracked changes.


The generated [`mutations.preview` reference](/document-api/reference/mutations/preview) and [`mutations.apply` reference](/document-api/reference/mutations/apply) list supported steps, limits, outputs, and failure codes.
