Report an issue

Real-time collaboration feature integration

This quick start guide will let you set up real-time collaboration inside your editor, including basic integration with other collaboration features.

After reading this article, we recommend visiting the “Learn more” sections in our documentation pages for the comments, track changes and revision history features to get more in-depth knowledge about them.

Complementary to this guide, we provide ready-to-use samples available for download. We prepared samples for all editor types (multi-root included) as well as for the React, Angular, and Vue.js integrations. You may use them as an example or a starting point for your integration.

# Sign up to the collaboration service

This is a paid feature. Contact us to receive an offer tailored to your needs. If you already have a valid license, please log into your user dashboard to access the feature settings.

The real-time collaboration feature needs a server to synchronize content between the clients, so to use it, you need to sign up to the collaboration service first. Refer to the CKEditor Cloud Services Collaboration – Quick Start guide for more details. There is also Premium features free trial available for testing purposes, that will provide the necessary backend.

# Preparing a custom editor setup

To use the real-time collaboration, you need to prepare a custom editor setup with several features enabled.

The easiest way to do that is by using the Builder. Pick a preset and start customizing your editor.

The Builder allows you to pick your preferred distribution method and framework. For this guide, we will use the “Vanilla JS” option with “npm” and a simple setup based on the “Classic Editor (basic)” preset, with the real-time collaboration features enabled.

In the “Features” section of the Builder (2nd step), make sure to:

  • make sure the “real-time” toggle next to the “Collaboration” group is on,
  • pick the entire “Collaboration” feature group.

Once you finish the setup, the Builder will provide you with the necessary HTML, CSS, and JavaScript code snippets. We will use those code snippets in the next step.

# Setting up a sample project

Once we have a custom editor setup we need a simple JavaScript project to run it. For this, we recommend cloning the basic project template from our repository:

npx -y degit ckeditor/ckeditor5-tutorials-examples/sample-project sample-project
cd sample-project
npm install

Then, install the necessary dependencies:

npm install ckeditor5
npm install ckeditor5-premium-features

This project template uses Vite under the hood and contains 3 source files that we will use: index.html, style.css, and main.js.

It is now the time to use our custom editor setup. Go to the “Installation” section of the Builder and copy the generated code snippets to those 3 files.

# Configure and initialize the editor

To finalize the editor configuration, you need to take a few additional steps in the main.js file:

If you do not have your CKEditor Cloud Services URLs ready, read more about these first in the CKEditor Cloud Services Collaboration – Quick Start guide.

const editorConfig = {
    /* ... */

    licenseKey: '<YOUR_LICENSE_KEY>',
    cloudServices: {
        tokenUrl: '<YOUR_CLOUD_SERVICES_TOKEN_URL>',
        webSocketUrl: '<YOUR_CLOUD_SERVICES_WEBSOCKET_URL>'
    },
    collaboration: {
        channelId: 'your-unique-channel-per-document'
    },

    /* ... */
};

const { EditorWatchdog } = ClassicEditor;
const watchdog = new EditorWatchdog( ClassicEditor );

// Replace ClassicEditor.create(document.querySelector('#editor'), editorConfig); with
watchdog.create( document.querySelector( '#editor' ), editorConfig );

Voilà! All users who open this page should be able to collaborate, working on the same rich text document at the same time.

# The channelId configuration property

The config.collaboration.channelId configuration property is an important property that controls which editor instances collaborate with one another. All clients created with the same channelId will be collaborating on the same content.

Each document must have a different channelId. This ID is usually the primary key of the document in the database or a unique identifier for a given form field. However, you are free to provide whatever identifier fits your scenario.

To start the collaboration on a new document, you need the document ID from the beginning, when the editor is created for the first time. If your application creates document IDs later, for instance when the form is submitted, you may need to provide another method to generate document IDs (for example, some random unique IDs generator).

The channelId needs to be unique in a given environment, so if you are using, for instance, staging and production environments, you may use the same channel ID in these. Since the environments are separated, these IDs will not interfere with one another.

Since editor content is linked with and only with the channel ID, it is possible to create an application where different views (forms, subpages, etc.) provide various sets of rich-text editable fields and have users collaborate with each other even though they have opened a different form, etc.

# Handling document data

All comments, suggestions, and revisions data is saved in CKEditor Cloud Services to make the integration work out of the box.

In this section, it is assumed that Cloud Services document storage is not enabled.

Document storage has numerous advantages and we recommend enabling it if available in your case.

Note that CKEditor Cloud Services does not save or load the document content. Instead, Cloud Services acts as a medium to handle the collaboration process. Content management should be handled by the application integration.

Your company’s policy may require you to store all data in your own data center or private cloud. We understand that. That is why we also provide the on-premises version of CKEditor Cloud Services. Contact us to learn more about it.

# Data initialization

When the first user opens the rich text editor for a certain document ID, their content is sent to CKEditor Cloud Services. In case of the example above it is:

<p>Let's edit this together!</p>

If you cannot or do not want to set the initial data straight in the HTML, use the initialData configuration option instead. Do not use editor.setData() (see below).

After the first user initializes the document and starts the editing session, every other user that connects to the same document will receive the content from CKEditor Cloud Services (while their local initial data is discarded).

From that moment on, every single change to the editor content is passed between Cloud Services and clients and the document for each client is updated. Any conflicting updates are resolved on the fly.

This is the reason why you should not use the editor.setData() or editor.data.set() methods when using the collaboration plugin. These simply overwrite the whole content of the editor (even if the data to set is the same as the current editor data). In real-time collaboration, this behavior can result in overwriting other clients’ local updates. In most cases, using editor.setData() and editor.data.set() in real-time collaboration is incorrect.

If you are sure that you understand and accept the behavior and side effects of setting the editor data this way, you can call editor.data.set() with the flag suppressErrorInCollaboration set to true:

editor.data.set( '<p>Your data</p>', { suppressErrorInCollaboration: true } );

This will let you overwrite the editor data without throwing an error.

# Saving data

Although CKEditor Cloud Services handles passing data between clients, you are still responsible for saving the final data in your database. The document is stored in the cloud by CKEditor Cloud Services only temporarily, as long as there are connected users. It means that the editor content should be saved in the database on your server before the last user disconnects – otherwise it will be lost. It is recommended to save the data automatically whenever it changes.

CKEditor 5 provides two utilities to make your integration simpler.

# The cloudDocumentVersion property

When your collaborative users are saving the same document at the same time, there might be a conflict. It might happen that one user will try to save an older document version, overwriting a newer one.

To prevent such race conditions, use the cloudDocumentVersion property.

editor.plugins.get( 'RealTimeCollaborationClient' ).cloudDocumentVersion

This property is simply a number, and a bigger value means a newer document version. This number should be stored in the database together with the document content. When a client wants to save the content, document versions should be compared. The document should only be saved when the version is higher. Otherwise, it means that the incoming data is an older version of the document and should be discarded.

CKEditor Cloud Services keeps the value of cloudDocumentVersion and makes sure that it is properly set whenever there is a new editing session for a given document.

# The Autosave plugin

Using the autosave feature with large documents may result in a lot of data being sent to your server and can have a negative impact on the editor user experience.

We recommend using the document storage feature instead.

The second helper is the Autosave plugin.

The autosave plugin triggers the save callback whenever the user changes the content. It takes care of throttling the callback execution to limit the number of calls to the database. It also automatically secures the user from leaving the page before the content is saved.

The autosave plugin is not enabler by default. To learn more about this plugin, refer to the Autosave feature guide.

# Autosave for revision history

If you are using the revision history feature, your autosave callback should take care of updating the recent revision data. You can add the autosave configuration to editorConfig in the main.js file as shown below:

const editorConfig = {
    /* ... */

    autosave: {
        save: async editor => {
            const revisionTracker = editor.plugins.get( 'RevisionTracker' );
            const currentRevision = revisionTracker.currentRevision;
            const oldRevisionVersion = currentRevision.toVersion;

            // Update the current revision with the newest document changes.
            await revisionTracker.update();

            // Check if the revision was updated.
            // If not, do not make an unnecessary call.
            if ( oldRevisionVersion === currentRevision.toVersion ) {
                return true;
            }

            // Use the document data saved with the revision instead of the editor data.
            // Revision data may slightly differ from the editor data when
            // real-time collaboration is involved.
            const documentData = await revisionTracker.getRevisionDocumentData( revisionTracker.currentRevision );

            // Use revision version instead of `cloudDocumentVersion`.
            const documentVersion = currentRevision.toVersion;

            // `saveData()` should save the document in your database.
            // `documentData` contains data for all roots.
            // You can save just `documentData.main` if you are using a single root,
            // or the whole object if you are using multiple roots.
            return saveData( documentData.main, documentVersion );
        }
    },

    /* ... */
}

You can read more about saving revisions in the Revision history guide.

# Reconnection process

During the real-time collaboration, it may happen that the internet connection will go down for some time. When this happens, the editor will switch into read-only mode. After the connection is back, the real-time collaboration plugin will try to reconnect the editor back to the editing session. This reconnection process may involve re-creating the editing session (if the reconnection happens after a longer period of time).

While one client is offline, other clients may perform changes to the document. When the offline client reconnects, the real-time editing plugin will make a decision if the reconnection is possible.

# Users selection

The real-time collaborative editing feature not only synchronizes the document content between all participants, but it also shows each user the list of all other users in real time. It works out of the box and does not require any additional configuration. This is the only part of the real-time collaborative editing plugin that provides a UI. Refer to the Users in real-time collaboration guide to learn more.

# Real-time collaboration samples

Please visit the ckeditor5-collaboration-samples GitHub repository to find various sample integrations of the real-time collaboration feature.