Contribute to this guide

Update to CKEditor 5 v29.x

When updating your CKEditor 5 installation, ensure all the packages are the same version to avoid errors.

For custom builds, you may try removing the package-lock.json or yarn.lock files (if applicable) and reinstalling all packages before rebuilding the editor. For best results, make sure you use the most recent package versions.

# Update to CKEditor 5 v29.1.0

Released on August 4, 2021.

For the entire list of changes introduced in version 29.1.0, see the release notes for CKEditor 5 v29.1.0.

Below are the most important changes that require your attention when upgrading to CKEditor 5 v29.1.0.

# Matcher pattern API change

Starting from v29.1.0, the Matcher feature deprecated matching style and class HTML attributes using attributes key-value pairs pattern.

The Matcher feature allows to match styles and classes by using dedicated styles and classes patterns. Since v29.0.0 it is also possible to match every possible value for these attributes by using the Boolean type with the true value. To avoid confusion about which pattern to use to match classes and styles, we decided to deprecate matching classes and styles using the attributes pattern.

Here is an example of changes you may need for proper integration with the Matcher feature new API:

// Old code.
new Matcher( {
    name: 'a',
    attributes: {
        'data-custom-attribute-1': /.*/,
        'data-custom-attribute-2': /.*/,
        style: true,
        class: true
    }
} );

// New code.
new Matcher( {
    name: 'a',
    attributes: {
        'data-custom-attribute-1': /.*/,
        'data-custom-attribute-2': /.*/
    },
    styles: true,
    classes: true
} );

Matcher pattern API change also improves how to define the link decorators (both manual decorator and automatic decorator). Similar to the Matcher feature API, you should define the style and class HTML attributes using the classes and styles properties.

Here is an example of changes you may need for proper integration with the link decorators API change:

// Old code.
ClassicEditor
    .create( ..., {
        // ...
        link: {
            decorators: {
                addGreenLink: {
                    mode: 'automatic',
                    attributes: {
                        class: 'my-green-link',
                        style: 'color:green;'
                    }
                }
            }
        }
    } )
// New code.
ClassicEditor
    .create( ..., {
        // ...
        link: {
            decorators: {
                addGreenLink: {
                    mode: 'automatic',
                    classes: 'my-green-link',
                    styles: {
                        color: 'green'
                    }
                }
            }
        }
    } )

# Update to CKEditor 5 v29.0.0

Released on July 7, 2021.

This migration guide enumerates the most important changes that require your attention when upgrading to CKEditor 5 v29.0.0 due to changes introduced in the Image plugin and some other image-related features.

For the entire list of changes introduced in version 29.0.0, see the release notes for CKEditor 5 v29.0.0.

To get to know the new editor UI for the image features, visit the image feature guide, especially:

# Inline images

Starting from v29.0.0, the existing Image plugin loads two independent plugins: ImageInline and ImageBlock, therefore both of them are included in all of the predefined editor builds by default.

  • The ImageInline is a newly introduced plugin supporting the inline <img> tag nested in text (for example inside a paragraph).
  • The ImageBlock maintains the functionality of the previous Image plugin before v29.0.0. In the model, it uses the imageBlock element (known as image before v29.0.0).

Note: It is possible to load only one of these plugins, but only when building the editor from source.

# Image caption

An image caption is no longer automatically shown when selecting the image widget. You can now toggle its visibility with a ToggleImageCaptionCommand executed by the 'toggleImageCaption' toolbar button, both registered by the ImageCaption plugin. The button is added to the default image toolbar in all the predefined editor builds.

To provide a valid data output, you can only add captions to block images. Adding a caption to an inline image will automatically convert it to a block image (which can be undone by the user).

# Image styles

Since the appearance of the image in the document depends on the image type (block/inline), the ImageStyle plugin is now in charge of switching between these types. Thus, we introduced the following changes:

  • A new set of buttons is available to manage the image type and appearance.

  • You can group the buttons provided by the ImageStyle plugin into dropdowns.

  • The name of the default block image style has changed from full to block (as the default style for the inline images is called inline), the default content styles for these images remain the same. The button label has also changed and now reads Centered image so that it reflects the actual appearance of the image. If you customized the default appearance of the block images, you can change the button label by modifying the existing image style.

  • The format of the config.image.styles has changed. You must wrap the list of the styles with the options array. Read more about the image.styles configuration.

    // Before v29.0.0.
    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            styles: [ 'inline', 'full', 'side' ]
        }
    } );
    
    // Since v29.0.0.
    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            styles: {
                options: [ 'inline', 'block', 'side' ]
            }
        }
    } );
    
  • The format of the imageStyle has changed. It must now provide information about the image types supporting a particular style. Read more about the ImageStyleOptionDefinition.

    // Before v29.0.0.
    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            styles: [ {
                name: 'alignLeft',
                title: 'Left aligned image',
                icon: objectLeft,
                className: 'image-style-align-left'
            } ]
        }
    } );
    
    // Since v29.0.0.
    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            styles: {
                options: [ {
                    name: 'alignLeft',
                    title: 'Left aligned image',
                    icon: objectLeft,
                    // Image types (names of the model elements) supporting this style.
                    modelElements: [ 'imageBlock', 'imageInline' ],
                    className: 'image-style-align-left'
                } ]
            }
        }
    } );
    
  • Several changes have been also made to the ImageStyle plugin API:

    • In the image style utilities module:
      • The defaultIcons were renamed to DEFAULT_ICONS.
      • The defaultStyles were renamed to DEFAULT_OPTIONS.
      • The normalizeImageStyles() function was removed from the public API.
    • The ImageStyleCommand#defaultStyle and ImageStyleCommand#styles were removed from the public API.

# Image toolbar

Until v29.0.0, custom editor builds without ImageStyle and ImageToolbar plugins were possible. Only block images were supported and captions were added by the user upon selecting the image.

Since v29.0.0, image styles and toolbar allow users to choose the type of image (inline or block) and give them a way to add or remove captions from block images via configurable buttons.

The user experience will degrade if either of these features is missing and this makes the image toolbar configuration essential.

Pre-configured editor builds come with ImageStyle and ImageToolbar plugins (and configuration) out-of-the-box. This information is mainly for developers who use custom editor builds in their integrations.

We recommend one of the following configurations as the minimum setup for the image toolbar:

  • For structured content editing (implemented by default in the classic, balloon, balloon block, and inline editor builds):

    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            toolbar: [
                'imageStyle:inline',
                'imageStyle:block',
                'imageStyle:side',
                '|',
                'toggleImageCaption',
                'imageTextAlternative'
            ]
        }
    } );
    
  • For document-like editing (implemented by default in the decoupled document build).

    Editor.create( document.querySelector( '#editor' ), {
        ...
        image: {
            toolbar: [
                'toggleImageCaption',
                'imageStyle:inline',
                // A dropdown containing `alignLeft` and `alignRight` options.
                'imageStyle:wrapText',
                // A dropdown containing `alignBlockLeft`, `block` (default) and  `alignBlockRight` options.
                'imageStyle:breakText'
            ]
        }
    } );
    

See the image feature guide to learn more about the configuration of the image toolbar.

# Inserting images

Since v29.0.0 inserting (also: pasting, dropping) an image in the middle of text will no longer split it if the ImageInline plugin is loaded (default). If you prefer the old behavior in your integration, you can specify this in the ImageInsert plugin configuration.

# Image utilities

  • The image utilities are now wrapped by the ImageUtils plugin.

    // Before v29.0.0.
    import { isImage } from './utils';
    
    const selectedElement = editor.model.document.selection.getSelectedElement();
    
    if ( isImage( selectedElement ) ) {
        // ...
    }
    
    // Since v29.0.0.
    // ...
    const imageUtils = this.editor.plugins.get( 'ImageUtils' );
    const selectedElement = editor.model.document.selection.getSelectedElement();
    
    if ( imageUtils.isImage( selectedElement ) ) {
        // ...
    }
    
  • The insertImage() function:

    • No longer requires the model model instance to run.
    • Allows Selectable as a second argument (before only Position was accepted).
    • Supports the optional imageType argument to force the type of the image to be inserted.
    // Before v29.0.0.
    import { insertImage } from './utils';
    
    const src = 'path/to/image.jpg';
    const model = ths.editor.model;
    const selection = model.document.selection;
    const position = model.createPositionAt( selection.getSelectedElement() );
    
    insertImage( model, { src }, position );
    
    // Since v29.0.0.
    const src = 'path/to/image.jpg';
    const selection = this.editor.model.document.selection;
    const imageUtils = this.editor.plugins.get( 'ImageUtils' );
    const imageType = 'imageBlock';
    
    imageUtils.insertImage( { src }, selection, imageType );
    
  • The isImage() function recognizes both inline and block images (before only block images).

  • There are two new helpers: isBlockImageView() and isInlineImageView().

We removed the following helpers from the public API:

  • getSelectedImageWidget(),
  • getViewImgFromWidget(),
  • isImageAllowed(),
  • isImageWidget(),
  • toImageWidget()

# EasyImage plugin

The EasyImage plugin is no longer automatically importing the Image plugin as a dependency. This allows using it alone with either ImageBlock or ImageInline without loading the other one.

This decoupling does not have an impact on integrations based on predefined builds or using the CKEditor 5 Builder.

However, for integrations that build the editor from source, this means that to get Easy Image working properly, the Image plugin (or either the ImageBlock or ImageInline plugin) must be imported separately:

import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import Image from '@ckeditor/ckeditor5-image/src/image';

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        plugins: [ EasyImage, Image, ... ],
        toolbar: [ 'uploadImage', ... ],

        // ...
    } )
    .then( ... )
    .catch( ... );

Check out the comprehensive installation guide to images in CKEditor 5 to learn more.

# CKFinder plugin

The CKFinder plugin is no longer automatically importing the Image plugin as a dependency. This allows using it alone with either ImageBlock or ImageInline without loading the other one.

This decoupling does not have an impact on integrations based on predefined builds or using the CKEditor 5 Builder.

However, for integrations that build the editor from source, this means that to get CKFinder working properly, you must import the Image plugin (or either the ImageBlock or ImageInline plugin) separately:

import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfider';
import Image from '@ckeditor/ckeditor5-image/src/image';

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        plugins: [ CKFinder, Image, ... ],
        toolbar: [ 'uploadImage', ... ],
        ckfinder: {
            // Feature configuration.
        }
    } )
    .then( ... )
    .catch( ... );

Check out the comprehensive installation guide to images in CKEditor 5 to learn more.