Contribute to this guide

guideMigration to CKEditor 5 v29.x

When updating your CKEditor 5 installation, make sure 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.

# Migration to CKEditor 5 v29.1.0

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

Listed 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’s also possible to match every possible value for these attributes by using Boolean type with true value. Therefore, to avoid confusion which pattern should be used to match classes and styles, we decided to deprecate matching classes and styles using 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 the link decorators should be defined (both manual decorator and automatic decorator). Similary to the Matcher feature API, style and class HTML attributes should be defined using respectively 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'
                    }
                }
            }
        }
    } )

# Migration to CKEditor 5 v29.0.0

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 changelog 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 (e.g. 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. Its visibility can now be toggled 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 of the predefined editor builds.

To provide a valid data output, captions can be added to block images only. 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, the following changes have been introduced:

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

  • There is a possibility to 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. The list of the styles must be wrapped 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 because 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 recommended one of the following configurations as the minimum set-up for the image toolbar:

  • For the purposes of the 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 the purposes of the 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, this can be specified 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 (previously 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 (previously only block images).

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

The following helpers were removed from the public API:

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

# EasyImage plugin

Please note that 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 official editor builds or using the CKEditor 5 online builder.

However, for integrations that build the editor from source, this means that in order 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

Please note that 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 official editor builds or using the CKEditor 5 online builder.

However, for integrations that build the editor from source, this means that in order to get CKFinder working properly, the Image plugin (or either the ImageBlock or ImageInline plugin) must be imported 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.