EngineeringMarch 12, 20262 min read

Track Changes in a React DOCX Editor

How to enable track changes in a React DOCX editor and automate document review with AI agents using DocxReviewer.

0.x postThis post uses the 0.x package names and APIs. For the current release see the 2.x docs.

Track changes model

Track changes records insertions, deletions, and formatting changes with an author and timestamp. Legal teams use these records for redlining. Compliance teams use them for audit trails, and editorial teams use them for review.

Suggesting mode

Set mode="suggesting" to record each edit as a tracked change:

import { DocxEditor } from "@eigenpal/docx-js-editor";
 
function ContractEditor({ buffer }: { buffer: ArrayBuffer }) {
  return (
    <DocxEditor
      documentBuffer={buffer}
      mode="suggesting"
      author="Current User"
    />
  );
}
  • Insertions use a green underline.
  • Deletions use a red strikethrough.
  • Consecutive edits by the same author form one revision.

You can switch between "editing" (direct edits), "suggesting" (tracked changes), and "viewing" (read-only).

Accepting and rejecting

The editor exposes acceptChange(from, to) and rejectChange(from, to) commands:

  • Accept: The insertion remains, or the deletion is removed.
  • Reject: The insertion is removed, or the deletion remains.

The sidebar shows accept/reject buttons on each tracked change card.

Round-trip fidelity

The editor preserves revisions when it opens documents that contain Word tracked changes. It saves editor revisions as Word tracked changes with revision metadata.

Try it

Automating track changes with AI agents

@docx-editor.dev/agents provides DocxReviewer, a headless API for programmatic document review. It runs without a Document Object Model (DOM), so you can use it in API routes, jobs, or scripts.

npm install @docx-editor.dev/agents

Basic usage

import { DocxReviewer } from "@docx-editor.dev/agents";
 
const reviewer = await DocxReviewer.fromBuffer(buffer, "AI Reviewer");
 
// Read as plain text (LLM-friendly format)
const text = reviewer.getContentAsText();
// [0] (h1) Service Agreement
// [1] The liability cap is $50k per incident.
 
// Comment
reviewer.addComment(1, "This cap seems too low.");
 
// Replace (creates a tracked change)
reviewer.replace(1, "$50k", "$500k");
 
// Export
const output = await reviewer.toBuffer();

The output is a DOCX with tracked changes and comments. You can open it in Word, Google Docs, or the editor in this post.

With an LLM

Read the document, send its text to a model, and apply the response as tracked changes:

import { DocxReviewer } from "@docx-editor.dev/agents";
import Anthropic from "@anthropic-ai/sdk";
 
const client = new Anthropic();
const reviewer = await DocxReviewer.fromBuffer(buffer, "Claude Reviewer");
 
const response = await client.messages.create({
  model: "claude-sonnet-4-7",
  max_tokens: 4096,
  messages: [{
    role: "user",
    content: `Review this contract. Return JSON with "comments" and "replacements":
${reviewer.getContentAsText()}`
  }],
});
 
const actions = JSON.parse(response.content[0].text);
reviewer.applyReview({
  comments: actions.comments,
  proposals: actions.replacements,
});
 
const output = await reviewer.toBuffer();

applyReview collects individual failures in result.errors instead of throwing for the complete review. One invalid paragraph index therefore does not discard other edits.

Agent review details

  • Headless: Runs on servers and edge functions without a DOM.
  • Text format: getContentAsText() returns [index] text for model prompts.
  • Text matching: Handles smart quotes, whitespace variations, and truncated model output.
  • Batch operations: Uses one applyReview() call for the complete review.
  • Word-compatible output: Writes revision marks, comments, and metadata that Word supports.

Full DocxReviewer API reference

Next steps