Track changes overview
The track changes feature (also known as suggestion mode) records edits as suggestions marked in the content instead of applying them directly. Reviewers can then follow who changed what and accept or decline each suggestion individually or all at once. It works both as a standalone plugin and as part of real-time collaboration.
You can test the track changes feature in the editor below. Use the toolbar dropdown to enable changes tracking and mass accept or decline suggestions. Use the side panel to work with individual changes.
In this mode, changes done by the users are marked in the content and shown as suggestions in the sidebar. Suggestions can be accepted or discarded by the users. The suggestion balloon is then closed and the change is no longer marked.
Suggestion annotations can be displayed in a sidebar or as inline balloons. Visit the display mode guide to learn how to configure the display mode. The mode specified for comments is also set for track changes.
This sample automatically switches between display modes according to the screen size. Resize the window and observe how the editor changes its appearance.
Apart from reading this guide, we encourage you to read a dedicated blog post which compares the track changes with revision history and another blog post discussing CKEditor 5’s collaboration features and their real-life implementations.
The track changes feature does not require real-time collaboration to work. If you prefer a more traditional approach to document editing, track changes can be added to CKEditor 5 just like any other plugin.
To learn how to integrate track changes as a standalone plugin, please refer to the Integrating track changes with your application guide.
If you are using the real-time collaboration feature, refer to the Real-time collaboration features integration guide.
After you run the track changes feature in your editor, you may want to learn more about it. Find out below how it works, how it can be configured, customized, and extended, to fit your application the best way possible.
The configuration for the track changes feature can be found in the TrackChangesConfig API reference.
Suggestions are always attached to some place in the document. To make sure that they will not be lost, the track changes plugin adds some special markup to the document:
<suggestion-start>and<suggestion-end>tags are added if the suggestion starts/ends in text,- otherwise, the following attributes are added to elements:
data-suggestion-start-before,data-suggestion-end-after,data-suggestion-start-after,data-suggestion-end-before.
Also, the <suggestion-td> tag is used for table cells pasting suggestions to separate old content (original) from the new content (pasted).
Read more about marker-to-data conversion to understand what data you may expect.
Examples of the possible markup:
Replacing the word “chocolate” with the word “ice-cream”:
<p>
I like
<suggestion-start name="insertion:e8ghd7:e390dk"></suggestion-start>ice-cream<suggestion-end name="insertion:e8ghd7:e390dk"></suggestion-end>
<suggestion-start name="deletion:ejd853:e390dk"></suggestion-start>chocolate<suggestion-end name="deletion:ejd853:e390dk"></suggestion-end>.
</p>Copy codeInserting image:
<figure class="image" data-suggestion-end-after="insertion:e1f0810:eohfu9" data-suggestion-start-before="insertion:e1f0810:eohfu9">
<img src="foo.jpg">
<figcaption>Image caption.</figcaption>
</figure>Copy codeAdding bold:
<p>
This is
<suggestion-start name="attribute:bold|ci1tcnk0lkep:ekeij:e3404"></suggestion-start>
<strong>important</strong>
<suggestion-end name="attribute:bold|ci1tcnk0lkep:ekeij:e3404"></suggestion-end>
.
</p>Copy codePasting a table cell with the text “New” into a table cell with the text “Old”:
<td ...>
<suggestion-td data-suggestion-end-after="insertion:..." data-suggestion-start-before="insertion:...">
<p>New</p>
</suggestion-td>
<suggestion-td data-suggestion-end-after="deletion:..." data-suggestion-start-before="deletion:...">
<p>Old</p>
</suggestion-td>
</td>Copy codeNote that if your application filters HTML content, for example, to prevent XSS, make sure to leave the suggestion tags and attributes in place when saving the content in the database. The suggestion markup is necessary for further editing sessions.
Suggestion attributes is additional data stored with the suggestions and used by other features. You can also use them to set and read custom data necessary by your custom features built around suggestions.
suggestion.setAttribute( 'isImportant', true );Copy codeYou can group multiple values in an object, using dot notation:
suggestion.setAttribute( 'customData.type', 'image' );
suggestion.setAttribute( 'customData.src', 'foo.jpg' );Copy codeAttributes set on the suggestion can be accessed through attribute property:
const isImportant = suggestion.attributes.isImportant;
const type = suggestion.attributes.customData.type;Copy codeYou can also observe attributes property or bind other properties to it:
myObj.bind( 'customData' ).to( suggestion, 'attributes', attributes => attributes.customData );Copy codeWhenever setAttribute() or removeAttribute() is called, the attributes property is re-set, and observables are refreshed. Using these fires the update method in an adapter.
If you need to get the editor data with all the existing suggestions accepted or discarded, please refer to the dedicated guide.
By default, the data returned by editor.getData() contains the markup for the suggestions as described above. It does not provide the markup that visually shows the suggestion highlights in the data (similarly to how they are shown in the editor).
It is possible to change the editor output using the showSuggestionHighlights option passed in editor.getData(). When set, the editor output will return suggestions similarly to how they are present inside the editor:
editor.getData( { showSuggestionHighlights: true } );Copy codeWill return:
<p>
<span class="ck-suggestion-marker ck-suggestion-marker-formatInline">
Foo
</span>
bar
</p>Copy codeThe output data generated using the showSuggestionHighlights option cannot be used to load the editor data (it cannot be passed to editor.setData())!
This feature should be used for preview purposes and can be used, for example, together with the export to PDF feature.
The export to PDF feature can be integrated with the suggestion highlights as shown below:
{
exportPdf: {
// More configuration of the Export to PDF.
// ...
dataCallback: editor => editor.getData( {
showSuggestionHighlights: true
} )
}
}Copy codeIf you would like to have track changes enabled by default, execute the trackChanges command, which toggles the track changes mode. It should be done after the editor instance is initialized:
ClassicEditor
.create( {
// Editor's configuartion.
// ...
} )
.then( editor => {
editor.execute( 'trackChanges' );
} )
.catch( error => console.error( error ) );Copy codeYou can disable the trackChanges command to prevent turning the track changes on or off. This can be useful for example when a particular user has permissions only to create suggestions in a given document. You can disable the command by calling Command#forceDisabled():
ClassicEditor
.create( {
// Editor's configuartion.
// ...
} )
.then( editor => {
editor.execute( 'trackChanges' );
editor.commands.get( 'trackChanges' ).forceDisabled( 'suggestionsMode' );
} )
.catch( error => console.error( error ) );Copy codeTo prevent a user from accepting or discarding suggestions, disable commands responsible for these actions:
ClassicEditor
.create( {
// Editor's configuartion.
// ...
} )
.then( editor => {
editor.execute( 'trackChanges' );
editor.commands.get( 'trackChanges' ).forceDisabled( 'suggestionsMode' );
editor.commands.get( 'acceptSuggestion' ).forceDisabled( 'suggestionsMode' );
editor.commands.get( 'acceptAllSuggestions' ).forceDisabled( 'suggestionsMode' );
editor.commands.get( 'discardAllSuggestions' ).forceDisabled( 'suggestionsMode' );
editor.commands.get( 'discardSuggestion' ).forceDisabled( 'suggestionsMode' );
} )
.catch( error => console.error( error ) );Copy codeKeep in mind that the 'suggestionsMode' identifier can be later used to enable commands using Command#clearForceDisabled().
Similarly to everywhere in the CKEditor 5 Ecosystem, we have used CSS Variables to let the developers customize the design of such UI elements as, for example, suggestion markers. You can override these properties with a .css file or place your customizations directly into the <head> section of your page, but in this case, you will need to use a more specific CSS selector than :root (like <body>).
If the editor is mounted inside a shadow root, override the variables on the shadow :host element instead, as described in the Shadow DOM guide.
Check out the color token files for the full list of customizable colors. You can also browse other files with CSS Variables in CKEditor 5.
Suggestion colors resolve from a shared diff token layer in the @ckeditor/ckeditor5-ui package, so you can customize them at two levels:
- Per surface – override the individual variables listed below to restyle the inline markers, the sidebar boxes, or the widget backgrounds separately.
- All diffs at once – override the shared
--ck-color-diff-*tokens (defined in theglobals/colors/_semantic-diff.csssheet) to recolor every change indicator consistently, including the revision history viewer and the AI review feed. Override them on:root, or on the shadow host, rather than on a narrower selector such asbody. The variables of the markers and boxes are resolved from these tokens on:root, so a narrower override does not reach them.
Here you can find the default values of the CSS variables used by the track changes feature:
:root {
/* Inline suggestion markers in the content. */
--ck-suggestion-marker-insertion-border-color: hsl(157 80% 34% / 50%);
--ck-suggestion-marker-insertion-active-border-color: hsl(157 80% 34% / 60%);
--ck-suggestion-marker-insertion-background-color: hsl(157 80% 34% / 20%);
--ck-suggestion-marker-insertion-active-background-color: hsl(157 80% 34% / 30%);
--ck-suggestion-marker-deletion-border-color: hsl(0 75% 52% / 40%);
--ck-suggestion-marker-deletion-active-border-color: hsl(0 75% 52% / 50%);
--ck-suggestion-marker-deletion-background-color: hsl(0 75% 52% / 20%);
--ck-suggestion-marker-deletion-active-background-color: hsl(0 75% 52% / 30%);
--ck-suggestion-marker-deletion-stroke-color: hsl(0 75% 43% / 60%);
--ck-suggestion-marker-format-border-color: hsl(191 60% 75%);
--ck-suggestion-marker-format-active-border-color: hsl(191 60% 60%);
/* Left border of the suggestion and comment boxes in the sidebar.
Its width is 0 by default, so set a width to show the colors below. */
--ck-annotation-type-border-width: 0px;
--ck-comment-box-border-color: hsl(42.1 95.4% 57.3%);
--ck-suggestion-box-insertion-border-color: hsl(145 33% 42.7%);
--ck-suggestion-box-deletion-border-color: hsl(8.4 69% 46.9%);
--ck-suggestion-box-format-border-color: var(--ck-color-base-active);
/* Suggestions on widgets (images, tables, media). */
--ck-suggestion-widget-insertion-background-color: hsl(128 71% 65% / 5%);
--ck-suggestion-widget-insertion-active-background-color: hsl(128 71% 50% / 7%);
--ck-suggestion-widget-deletion-background-color: hsl(345 71% 65% / 5%);
--ck-suggestion-widget-deletion-active-background-color: hsl(345 71% 45% / 7%);
--ck-suggestion-widget-format-background-color: hsl(191 90% 40% / 9%);
--ck-suggestion-widget-format-active-background-color: hsl(191 90% 40% / 16%);
--ck-suggestion-widget-th-insertion-background-color: hsl(128 71% 65% / 12%);
--ck-suggestion-widget-th-insertion-active-background-color: hsl(128 71% 50% / 14%);
--ck-suggestion-widget-th-deletion-background-color: hsl(345 71% 65% / 14%);
--ck-suggestion-widget-th-deletion-active-background-color: hsl(345 71% 45% / 16%);
}Copy codeThese colors were refreshed to align with the new editor theme. Because suggestion markers also apply to published .ck-content, override the variables above (or the shared --ck-color-diff-* tokens) if you need to keep the previous appearance. The previous theme also showed the left border of the sidebar boxes, which --ck-annotation-type-border-width: 3px restores.
When you copy, cut or drag&drop content containing suggestions, all selected content will be kept in the clipboard pipeline as-is in the editor, meaning that e.g. content in deletion suggestions will be retained.
This behavior can be changed using the config.trackChanges.clipboardMode configuration option. If you set it to accept, clipboard pipeline will automatically accept most suggestions, so the clipboard will contain content as if suggestions were accepted.
Note, that there’s a limitation regarding some block format suggestions (e.g. blockquote or code block) - these suggestions cannot be automatically accepted in clipboard and will be dropped.
Check the track changes API documentation for detailed information about the track changes API. Making yourself familiar with the API may help you understand the code snippets.
The suggestions annotations are highly customizable. Please refer to the Annotation customization guide to learn more.
If you provide your own plugins, you may want to integrate these custom features with track changes mode.