Report an issue

guideAnnotations display mode

There are three built-in annotations UIs that provide various ways to display comment threads and suggestion annotations: the wide sidebar, the narrow sidebar and inline balloons. These UIs can also be displayed together for more advanced scenarios where various annotation sources (comments, suggestions) are connected to different UIs. It is also possible to create your own annotations UI.

To create a build that includes the comments plugin, refer to the comments integration guide. Alternatively, use an editor from one of the provided samples.

# Inline balloons

Inline balloon display mode is designed for narrow screens like mobile devices and UIs where the WYSIWYG editor is used to edit a small part of the content.

CKEditor 5 WYSIWYG editor comments with the inline display mode

Inline display mode is the default solution. It is used when the sidebar configuration is not specified.

Even if the sidebar configuration is set, you can still dynamically switch to the inline display mode:

editor.plugins.get( 'AnnotationsUIs' ).switchTo( 'inline' );

// The sidebar container is not removed automatically,
// so it is up to your integration to hide it (or manage in another way).
document.querySelector( '#sidebar' ).style.display = 'none';

Currently, the inline balloons UI only works with editor annotations and it cannot be used together with the context feature.

# Wide sidebar

The wide sidebar can fit the largest amount of information — the user is able to see the beginning and the end of each discussion, as well as the full discussion for the currently selected marker. It is the recommended solution whenever you have enough space for it.

CKEditor 5 WYSIWYG editor comments with the wide sidebar display mode

To use the wide sidebar for displaying comments and suggestion annotations, first prepare a proper HTML structure:

<html>
    <head>
        <style type="text/css">
            #container {
                display: flex;
                position: relative;
            }

            #container .ck.ck-editor {
                width: 100%;
                max-width: 700px;
            }

            #sidebar {
                min-width: 300px;
                padding: 0 10px;
            }
        </style>
    </head>
    <body>
        <div id="container">
            <div id="editor"></div>
            <div id="sidebar"></div>
        </div>
    </body>
</html>

Then, initialize the rich text editor using a build that includes the comments plugin.

In the configuration, set the editor to use the <div id="sidebar"> element as the comments container.

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // Your configuration.
        ...
        sidebar: {
            container: document.querySelector( '#sidebar' )
        }
    } );

After setting the configuration as shown in the example above, the wide sidebar display mode will be used. If the display mode was changed, you can change it back:

editor.plugins.get( 'AnnotationsUIs' ).switchTo( 'wideSidebar' );

You can also set the sidebar container dynamically:

editor.plugins.get( 'Sidebar' ).setContainer( element );

If the sidebar container has already been set, all the items inside it will be moved to the new container.

# Narrow sidebar

The narrow sidebar is a compromise between the wide sidebar and the inline balloons. It does not take as much space as the wide sidebar, but contains more information than inline annotations. The user will immediately see when multiple comment threads are added to the same spot as well as how many comments are added.

CKEditor 5 WYSIWYG editor comments with the narrow sidebar display mode

The HTML structure for the wide and narrow sidebars is very similar. The only difference is that you need to set a different min-width CSS property for the #sidebar element:

<html>
    <head>
        <style type="text/css">
            #container {
                display: flex;
                position: relative;
            }

            #container .ck.ck-editor {
                width: 100%;
                max-width: 700px;
            }

            #sidebar {
                min-width: 65px;
                padding: 0 10px;
            }
        </style>
    </head>
    <body>
        <div id="container">
            <div id="editor"></div>
            <div id="sidebar"></div>
        </div>
    </body>
</html>

Then, initialize the rich text editor using a build that includes the comments plugin and switch the UI to the narrowSidebar mode. Note that you need to switch the UI type manually, since the wide sidebar will be displayed by default.

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // Your configuration.
        ...
        sidebar: {
            container: document.querySelector( '#sidebar' )
        }
    } )
    .then( editor => {
        editor.plugins.get( 'AnnotationsUIs' ).switchTo( 'narrowSidebar' );
    } );

# Multiple UIs

Annotations were designed to support displaying various annotations UIs at the same time. This allows you to display different annotation sources in various places, for example, displaying comments in the wide sidebar while showing inline balloons for suggestions.

To activate multiple UIs at the same time, the filtering function should be passed to the activate() method. The function specifies which annotations are controlled by a given UI. Note that one annotation can not be managed by multiple AnnotationsUI at the same time. If an annotation is not accepted by any of annotations UIs, then that annotation will not be shown.

To use a combination of annotations UIs for displaying comments and suggestion annotations, first prepare a proper HTML structure (for demonstration purposes, the wide sidebar is used):

<html>
    <head>
        <style type="text/css">
           #container {
                display: flex;
                position: relative;
            }

            #container .ck.ck-editor {
                width: 100%;
                max-width: 700px;
            }

            #sidebar {
                min-width: 300px;
                padding: 0 10px;
            }
        </style>
    </head>
    <body>
        <div id="container">
            <div id="editor"></div>
            <div id="sidebar"></div>
        </div>
    </body>
</html>

Then, initialize the rich text editor using a build that includes both the comments and track changes features, and activate the two annotations UIs.

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // Your configuration.
        sidebar: {
            container: document.querySelector( '#sidebar' )
        }
    } )
    .then( editor => {
        const annotationsUIs = editor.plugins.get( 'AnnotationsUIs' );

        // Deactivate all UIs first as the `activate()` method might not deactivate all UIs.
        annotationsUIs.deactivateAll();

        annotationsUIs.activate( 'wideSidebar', annotation => annotation.type === 'comment' );
        annotationsUIs.activate( 'inline', annotation => annotation.type !== 'comment' );
    } );

# All display modes in action

The code snippet below allows for switching between all available display modes.

Complementary to this guide, we provide a ready-to-use sample available for download. You may use the sample as an example or as a starting point for your own integration. Note that this sample covers not only comments but also track changes suggestions.

The code snippet below needs a build that includes the comments and track changes plugins. To create it, refer to the comments integration guide and the track changes integration guide.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>CKEditor 5 automatic display mode switching</title>
        <style type="text/css">
            #container {
                display: flex;
                position: relative;
            }

            #container .ck.ck-editor {
                width: 100%;
                max-width: 700px;
            }

            #sidebar {
                min-width: 300px;
                padding: 0 10px;
                transition: min-width .4s ease-out-in;
            }

            #sidebar.narrow {
                min-width: 65px;
            }

            #sidebar.hidden {
                display: none;
            }
        </style>
    </head>
    <body>
        <div class="buttons">
            <button id="inline">Inline</button>
            <button id="narrow">Narrow sidebar</button>
            <button id="wide">Wide sidebar</button>
            <button id="wide-inline">Wide sidebar + inline</button>
        </div>
        <div id="container">
            <div id="editor">
                <p>Try to add a comment and resize the browser window!</p>
            </div>
            <div id="sidebar"></div>
        </div>

        <script src="../build/ckeditor.js"></script>
        <script>
            ClassicEditor
                .create( document.querySelector( '#editor' ), {
                    toolbar: {
                        items: [ 'bold', 'italic', '|', 'comment' ]
                    },
                    sidebar: {
                        container: document.querySelector( '#sidebar' )
                    }
                } )
                .then( editor => {
                    const users = editor.plugins.get( 'Users' );

                    // You need to define "me" to be able to add a comment.
                    users.addUser( { id: 'user-1', name: 'Joe Doe' } );
                    users.defineMe( 'user-1' );

                    const annotationsUIs = editor.plugins.get( 'AnnotationsUIs' );
                    const sidebarElement = document.querySelector( '#sidebar' );
                    const inlineButton = document.querySelector( '#inline' );
                    const narrowButton = document.querySelector( '#narrow' );
                    const wideButton = document.querySelector( '#wide' );
                    const wideAndInlineButton = document.querySelector( '#wide-inline' );

                    function markActiveButton( button ) {
                        [ inlineButton, narrowButton, wideButton, wideAndInlineButton ]
                            .forEach( el => el.classList.toggle( 'active', el === button ) );
                    }

                    function switchToInline() {
                        markActiveButton( inlineButton );
                        sidebarElement.classList.remove( 'narrow' );
                        sidebarElement.classList.add( 'hidden' );
                        annotationsUIs.switchTo( 'inline' );
                    }

                    function switchToNarrowSidebar() {
                        markActiveButton( narrowButton );
                        sidebarElement.classList.remove( 'hidden' );
                        sidebarElement.classList.add( 'narrow' );
                        annotationsUIs.switchTo( 'narrowSidebar' );
                    }

                    function switchToWideSidebar() {
                        markActiveButton( wideButton );
                        sidebarElement.classList.remove( 'narrow', 'hidden' );
                        annotationsUIs.switchTo( 'wideSidebar' );
                    }

                    function switchToWideSidebarAndInline() {
                        markActiveButton( wideAndInlineButton );
                        sidebarElement.classList.remove( 'narrow', 'hidden' );

                        annotationsUIs.deactivateAll();
                        annotationsUIs.activate( 'wideSidebar', annotation => annotation.type === 'comment' );
                        annotationsUIs.activate( 'inline', annotation => annotation.type !== 'comment' );
                    }

                    editor.ui.view.listenTo( inlineButton, 'click', () => switchToInline() );
                    editor.ui.view.listenTo( narrowButton, 'click', () => switchToNarrowSidebar() );
                    editor.ui.view.listenTo( wideButton, 'click', () => switchToWideSidebar() );
                    editor.ui.view.listenTo( wideAndInlineButton, 'click', () => switchToWideSidebarAndInline() );

                    // Set wide sidebar as default.
                    switchToWideSidebar();
                } )
                .catch( error => console.error( error ) );
        </script>
    </body>
</html>

# Demo

The following sample showcases the snippet above:

# Custom UI

In addition to the built-in annotations UIs, it is also possible to create a custom UI that will display annotations in a way that is better suited to your application.

Note that annotations UI should implement the AnnotationsUI interface.

The frame of an annotations UI should look like this:

import ContextPlugin from '@ckeditor/ckeditor5-core/src/contextplugin';

class CustomAnnotationsUI extends ContextPlugin {
    constructor( ...args ) {
        super( ...args );

        // The `activeAnnotation` property should be defined as an observable property.
        this.set( 'activeAnnotation', null );
    }

    // The `attach()` method should create everything needed for the UI and
    // attach all listeners. This method is called when the UI is activated.
    //
    // The observable collection of annotations is passed as the first argument,
    // and the annotations UI is responsible for reacting to its changes.
    attach( annotations ) {
        // Do something when an annotation is added.
        this.listenTo( annotations, 'add', ( evt, annotation ) => { ... } );

        // Do something when an annotation is removed.
        this.listenTo( annotations, 'remove', ( evt, annotation ) => { ... } );
    }

    // The `detach()` method should destroy the UI and remove all listeners.
    // This method is called when the UI is deactivated.
    detach() {
        this.stopListening();
    }

    // The `setActiveAnnotation()` method should set or unset the active annotation.
    setActiveAnnotation( annotation ) {
        if ( this.activeAnnotation ) {
            this.activeAnnotation.isActive = false;

            // You can do something in your UI with the annotation that is no longer active.
        }

        this.activeAnnotation = annotation;
        this.activeAnnotation.isActive = true;

        // You can do something in your UI to highlight the active annotation.
    }
}

Then, initialize the rich text editor using a build that includes the comments feature, register your custom UI, and activate it.

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        // Your configuration.
        plugins: [
            // Your plugins.
            CustomAnnotationsUI
        ],
        sidebar: {
            container: document.querySelector( '#sidebar' )
        }
    } )
    .then( editor => {
        const annotationsUIs = editor.plugins.get( 'AnnotationsUIs' );
        const customAnnotationsPlugin = editor.plugins.get( 'CustomAnnotationsPlugin' );

        annotationsUIs.register( 'customUI', customAnnotationsPlugin );
        annotationsUIs.switchTo( 'customUI' );
    } );