# Comparing documents

Use the `DocumentCompare` plugin of the [Editing Toolkit](../editing-toolkit-overview.md) to update the editor content with HTML produced outside the editor, for example by an AI agent, a script, or a converted Word file. It compares that HTML with the current document and applies only the differences, as granular changes. Unlike `editor.setData()`, it does not replace the whole document, so comments, [Track Changes](../../collaboration/track-changes/track-changes.md), [revision history](../../collaboration/revision-history/revision-history.md), and [real-time collaboration](../../collaboration/real-time-collaboration/real-time-collaboration.md) keep working.

You can apply the changes directly or as Track Changes suggestions. To let users review the changes before applying them, pair it with the [Proposed changes UI](editing-toolkit-proposing-changes.md).

> **Unlock this feature with the Custom Plan**
>
> Try this feature for **free during your trial** — no credit card required.\
> After the trial, it’s available as a premium add-on in the [CKEditor Custom Plan](https://ckeditor.com/pricing/).
>
> [Sign up for a free trial ](https://portal.ckeditor.com/checkout?plan=free)[Contact sales](https://ckeditor.com/contact-sales/)

<a id="demo"></a>

## Demo

The left editor holds a new version of the policy shown on the right, written by an AI agent. It rewrites sentences, bolds key terms, changes a table cell, and adds a table row, a list item, and a paragraph. Edit it further if you like. Choose Apply as suggestions to record the differences as Track Changes suggestions in the original document on the right. Reset the demo to start over.

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

This demo presents a limited set of features. Visit the [feature-rich editor example](../../../examples/builds-custom/full-featured-editor.md) to see more in action.

<a id="how-it-works"></a>

## How it works

![Sequence of comparing documents: your application takes a snapshot through the toolkit, sends its content to your code or service, receives the processed HTML, compares it with the snapshot, and applies the patch directly or as suggestions.](https://ckeditor.com/docs/ckeditor5/latest/assets/img/editing-toolkit-compare-workflow.svg)

Comparing documents takes four steps:

1. **Snapshot** – [`makeSnapshot()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-makeSnapshot) captures the current document. See [What a snapshot holds](#what-a-snapshot-holds).
2. **Process** – Your code or service transforms that HTML. It can be a script, a backend service, or CKEditor AI, as described in the [Using with CKEditor AI](../use-cases/editing-toolkit-with-ckeditor-ai.md) guide. For your own AI agents, use [Coauthor](../editing-toolkit-coauthor.md), which helps them produce content the editor accepts.
3. **Compare** – [`computePatch()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-computePatch) compares the processed HTML with the snapshot and returns a patch for the whole document.
4. **Apply** – [`applyPatch()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-applyPatch) applies the patch, directly or as suggestions.

Before applying anything, `DocumentCompare` validates the patch against a copy of the document. If validation fails, it throws an error and leaves the document unchanged.

<a id="installation"></a>

## Installation

After [installing the editor](../../../getting-started/installation/cloud/quick-start.md), add the `DocumentCompare` plugin to your plugin list:

**NPM**

```js
import { ClassicEditor } from 'ckeditor5';
import { DocumentCompare } from 'ckeditor5-premium-features';

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		licenseKey: '<YOUR_LICENSE_KEY>',
		plugins: [ DocumentCompare, /* ... */ ]
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

**CDN**

```js
const { ClassicEditor } = CKEDITOR;
const { DocumentCompare } = CKEDITOR_PREMIUM_FEATURES;

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		licenseKey: '<YOUR_LICENSE_KEY>',
		plugins: [ DocumentCompare, /* ... */ ]
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

To apply changes directly, you do not need Track Changes or an AI service. To apply changes as suggestions, also load the `TrackChanges` plugin and complete its setup, including registering the current user. Loading the plugin alone is not enough. See the [Track Changes integration](../../collaboration/track-changes/track-changes-integration.md) guide for details.

`DocumentCompare` does not require `config.collaboration.channelId`. If it is set, `DocumentCompare` uses it to check that a patch is applied to the document it was computed for. Setting it does not enable Cloud Services or real-time collaboration. The [Proposed changes UI](editing-toolkit-proposing-changes.md) requires it.

<a id="what-a-snapshot-holds"></a>

## What a snapshot holds

A snapshot is a plain object that records the document at one point in time:

* `content` – The HTML of each editor root, keyed by root name. In a single-root editor, it holds only the `main` root. This is what your processor receives and changes.
* `editorConfig` – The features the editor has loaded and their configuration, such as the allowed heading levels or font sizes, plus the CKEditor 5 version. Pass it to your AI model or agent together with the content, so it produces only content the editor can hold. To check the output against the real editor instead of a description, use [Coauthor](../editing-toolkit-coauthor.md).
* The document version and the editing session in which the snapshot was taken. `DocumentCompare` uses them to merge the result with edits made in the meantime. See [Editing while the content is processed](#editing-while-the-content-is-processed).

The snapshot is safe to serialize. You can send it to a backend, store it, and pass it back to `computePatch()` when the result is ready.

<a id="running-a-comparison"></a>

## Running a comparison

The following example runs the four steps one by one. Replace `externalDataProcessing()` with your own processing:

```js
const documentCompare = editor.plugins.get( 'DocumentCompare' );

// 1. Capture the document.
const snapshot = documentCompare.makeSnapshot();

// 2. Process the content.
const processed = await externalDataProcessing( snapshot.content );

// 3. Compare the result with the snapshot.
const patch = documentCompare.computePatch( snapshot, processed );

// 4. Apply the differences.
try {
	documentCompare.applyPatch( patch );
} catch ( error ) {
	// See the "Handling errors" section.
	console.error( error );
}
```

When you want to apply the result immediately, [`process()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-process) runs all four steps in one call. It passes the snapshot to your callback and applies the returned data:

```js
await documentCompare.process( async snapshot => externalDataProcessing( snapshot.content ) );
```

<a id="editing-while-the-content-is-processed"></a>

## Editing while the content is processed

Processing may take a while, for example when it calls an AI service. Users can keep editing in the meantime. Keep the original snapshot while the processor works, then compare its result with that snapshot. When the changes are applied, `DocumentCompare` merges them with all edits made after the snapshot, including edits of other users in real-time collaboration.

The changes are adjusted to these edits in the same way as real-time collaboration merges edits of different users. For example, if a user edits another part of the paragraph that the patch changes, both changes are kept.

A patch can only be applied to the same document, in the same editing session in which the snapshot was taken:

* Without real-time collaboration, the editing session lasts as long as the editor instance. Reloading the page or creating the editor again starts a new session.
* With real-time collaboration, the editing session is the real-time collaboration session.

There is no time limit. A long-running job can apply its result as long as the editing session continues. If the session has ended, applying the patch throws the `documentcompare-session-mismatch` error.

<a id="comparing-content-with-data-id-attributes"></a>

## Comparing content with `data-id` attributes

By default, the snapshot HTML has a `data-id` attribute on every structural element, such as a paragraph, heading, or list item. The comparison uses these IDs to pair the elements of the snapshot and the processed content. This gives the best results, so use this mode whenever your processing can keep the IDs.

Your processor must follow these rules:

* Keep the `data-id` of every element that stays in the document, even if you change its content.
* Do not add a `data-id` to new elements.
* Do not reuse the ID of an existing element for a different element.

For example, a processor rewrites the second paragraph and adds a third one:

```html
<!-- Snapshot. -->
<h2 data-id="e1">Onboarding</h2>
<p data-id="e2">Contact the IT helpdesk on your first day.</p>

<!-- Processed content. -->
<h2 data-id="e1">Onboarding</h2>
<p data-id="e2">Contact the Service Desk on your first day.</p>
<p>Your manager will introduce you to the team.</p>
```

The actual ID values are generated, so always take them from the snapshot.

> **Warning**
>
> If your content already uses the `data-id` attribute, it may conflict with `DocumentCompare`. See the [Conflicts with the `data-id` attribute](../../ai/ckeditor-ai-overview.md#conflicts-with-the-data-id-attribute) section of the CKEditor AI overview for details.

If the processor removes or changes the ID of an element it keeps, the comparison cannot pair the element with its original. It treats it as a removed element and a new one, so the change covers the whole element instead of only the edited text. If your processor cannot keep the IDs reliably, compare the content without them, as described in the next section.

<a id="comparing-content-without-data-id-attributes"></a>

## Comparing content without `data-id` attributes

Use this mode when the new content cannot carry the IDs. Typical cases:

* **Processing through another format** – Your processor works on Markdown or plain text instead of HTML, for example an AI service that takes Markdown as input. Converting to Markdown and back drops the attributes.
* **Comparing an external version** – The new content was not created from the snapshot, so it has no IDs at all. For example, someone edited a copy of the document in Microsoft Word, and you converted it to HTML with the [Import from Word REST API](../../converters/import-word/import-word.md#rest-api). The [Scripts and external versions](../use-cases/editing-toolkit-deterministic-scripts.md) guide shows this case in a demo.
* **Unreliable processors** – A tool in your pipeline rewrites the HTML and does not always keep the attributes.

In these cases, take the snapshot with the `useDataIds` option set to `false`. The comparison then pairs the elements by how similar their content is:

```js
const snapshot = documentCompare.makeSnapshot( { useDataIds: false } );
const processed = await externalDataProcessing( snapshot.content );

const patch = documentCompare.computePatch( snapshot, processed );

documentCompare.applyPatch( patch );
```

To compare an external version, you do not process the snapshot content at all. Take the snapshot immediately before the comparison, so it matches the current document, and compare it with the converted HTML:

```js
const snapshot = documentCompare.makeSnapshot( { useDataIds: false } );
const patch = documentCompare.computePatch( snapshot, importedHtml, { asSuggestions: true } );

documentCompare.applyPatch( patch );
```

The `process()` method accepts the same `useDataIds` option.

Pairing by content is slower and less precise than pairing by IDs. Keep the IDs whenever your processing allows it.

<a id="applying-changes-directly-or-as-suggestions"></a>

## Applying changes directly or as suggestions

By default, `DocumentCompare` applies changes directly to the content. To apply them as [Track Changes](../../collaboration/track-changes/track-changes.md) suggestions instead, pass the `asSuggestions` option to `computePatch()` or `process()`:

```js
const patch = documentCompare.computePatch( snapshot, processed, {
	asSuggestions: true
} );

documentCompare.applyPatch( patch );
```

Applying changes as suggestions is not the same as reviewing them. Suggestions are already in the document, and users accept or reject them with the Track Changes UI. To let users decide before anything reaches the document, use the [Proposed changes UI](editing-toolkit-proposing-changes.md).

<a id="marking-changes-as-made-by-ai"></a>

### Marking changes as made by AI

If the processed content comes from AI, set the `aiSource` option of `computePatch()` or `process()` to a value that identifies your AI feature, for example `'myAgent'`. The resulting suggestions and revisions are then marked as made by AI, as configured by [`config.trackChanges.showAISource`](../../../api/module_track-changes_trackchangesconfig-TrackChangesConfig.md#member-showAISource) and [`config.revisionHistory.showAISource`](../../../api/module_revision-history_revisionhistoryconfig-RevisionHistoryConfig.md#member-showAISource):

```js
const patch = documentCompare.computePatch( snapshot, processed, {
	asSuggestions: true,
	aiSource: 'myAgent'
} );

documentCompare.applyPatch( patch );
```

The `aiSource` option only labels the changes. It does not change who the suggestions belong to. Suggestions are always created by the current user, as defined in the [Track Changes integration](../../collaboration/track-changes/track-changes-integration.md). In the browser, this is the person who runs the comparison.

<a id="comparing-inline-roots"></a>

## Comparing inline roots

A multi-root editor can have [inline roots](../../../getting-started/setup/root-types.md#inline-root), which accept only text and inline formatting, for example for a document title. The content of an inline root has no block element, so the snapshot wraps it in an artificial `<p data-id="...">` element. The wrapper keeps its `data-id` even when the snapshot is taken with `useDataIds: false`. Process only the inline content inside the wrapper, without adding paragraphs, lists, or other blocks.

<a id="handling-errors"></a>

## Handling errors

The following errors can occur when you apply a patch. `applyPatch()` checks for them before it changes anything, so the document stays unchanged.

* `documentcompare-channel-mismatch` – The patch is applied to a different document than the one the snapshot was taken from. Make sure you apply it in the right editor.
* `documentcompare-session-mismatch` – The snapshot was taken in a previous editing session, for example before a page reload. Take a new snapshot and process the content again.
* `documentcompare-invalid-patch` – The patch cannot be applied to the current document. Take a new snapshot and process the content again.
* `documentcompare-unsupported-patch` – The patch contains operations that `computePatch()` never produces. Apply only patches returned by `computePatch()`.
* `documentcompare-no-track-changes` – Changes are applied as suggestions, but the editor does not load Track Changes. Set up Track Changes or apply the changes directly.

If the patch passes validation but applying it to the document fails, `applyPatch()` throws the `documentcompare-apply-failed` error. In this case, the patch might be applied only partially.

<a id="related-features"></a>

## Related features

* [Proposing changes](editing-toolkit-proposing-changes.md) – Present the computed changes to users and let them accept or reject each one.
* [Editing Toolkit overview](../editing-toolkit-overview.md) – Learn about all components of the toolkit.
* [Track Changes](../../collaboration/track-changes/track-changes.md) – Required only when changes are applied as suggestions.

<a id="common-api"></a>

## Common API

The [`DocumentCompare`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md) plugin provides the following methods:

* [`makeSnapshot()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-makeSnapshot) – Captures the current document.
* [`computePatch()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-computePatch) – Compares the processed content with a snapshot and returns a patch for the whole document.
* [`applyPatch()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-applyPatch) – Applies a patch to the document.
* [`process()`](../../../api/module_ai_toolkit_documentcompare-DocumentCompare.md#function-process) – Runs the whole pipeline in one call.

The plugin uses the following types:

* [`DocumentCompareSnapshot`](../../../api/module_ai_toolkit_documentsnapshot-DocumentCompareSnapshot.md) – The captured document.
* [`DocumentComparePatch`](../../../api/module_ai_toolkit_documentcompare-DocumentComparePatch.md) – The differences between a snapshot and the processed content.
* [`DocumentCompareSnapshotOptions`](../../../api/module_ai_toolkit_documentcompare-DocumentCompareSnapshotOptions.md), [`DocumentCompareComputePatchOptions`](../../../api/module_ai_toolkit_documentcompare-DocumentCompareComputePatchOptions.md), and [`DocumentCompareProcessOptions`](../../../api/module_ai_toolkit_documentcompare-DocumentCompareProcessOptions.md) – The options of the methods above.

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../../llms.txt)
