Contribute to this guide

Downcast helpers – model to view conversion

This article lists all editor helpers available in the downcast conversion.

# Element to element conversion helper

Converting a model element to a view element is the most common case of conversion. It is used to create view elements like <p> or <h1>, that we call “container elements.”

When using the elementToElement() helper, a single model element will be converted to a single view element. The children of this model element need to have their own converters defined and the engine will recursively convert them and insert into the created view element.

# Basic element to element conversion

If you want to convert a model element to a simple view element without any additional attributes, simply provide their names through the converter, as shown in this example:

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: 'paragraphSeparator',
        view: 'hr'
} );

# Using view element definition

Sometimes you may need to output a view element that has certain attributes, like a class name. To achieve this, you can provide an element definition in the view property:

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: 'fancyParagraph',
        view: {
            name: 'p',
            classes: 'fancy'
        }
    } );

Check out the ElementDefinition documentation for more details.

# Creating a view element using a callback

An alternative way to write a converter from the previous section using a callback would look as follows:

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: 'fancyParagraph',
        view: ( modelElement, { writer } ) => {
            return writer.createContainerElement(
                'p', { class: 'fancy' }
            );
        }
    } );

Here, the second parameter of the view callback is the DowncastConversionApi object. It contains many properties and methods that can be useful when writing a more complex converters.

The callback should return a single container element. This element should not contain any children except UI elements. If you want to create a richer structure, use the elementToStructure() method.

# Handling model elements with attributes

If the view element does not only depend on the model element itself but also on its attributes, you need to specify these attributes in the model property.

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: {
            name: 'heading',
            attributes: [ 'level' ]
        },
        view: ( modelElement, { writer } ) => {
            return writer.createContainerElement(
                'h' + modelElement.getAttribute( 'level' )
            );
        }
    } );

If you forget to specify these attributes, the converter will still work for the insertion of the model element part but it will not handle any changes of the attribute value.

# Changing converter priority

In case there are other converters with overlapping model patterns already present, you can prioritize your converter to override these. To do that, use the converterPriority property:

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: 'userComment',
        view: 'div'
    } );

editor.conversion
    .for( 'downcast' )
    .elementToElement( {
        model: 'userComment',
        view: 'article',
        converterPriority: 'high'
    } );

In the example above, the first converter has no explicitly set priority hence it assumes default priority, which is normal. The second one overrides it by setting the priority to high. Using both of these converters at once will result in the <userComment> element being converted to an <article> element.

This solution may also be handy if you want your converter to act as a fallback when other converters for a given element are not present (for example, a plugin has not been loaded). It can be easily achieved by setting the converterProperty to low.

# Element to structure conversion helper

Convert a single model element to multiple view elements (a structure of view elements).

# Handling empty model elements

To convert a single model element horizontalLine to the following structure:

<div class="horizontal-line">
    <hr />
</div>

you can use a converter similar to this one:

editor.conversion
    .for( 'downcast' )
    .elementToStructure( {
        model: 'horizontalLine',
        view: ( modelElement, { writer } ) => {
            return writer.createContainerElement( 'div', { class: 'horizontal-line' }, [
                writer.createEmptyElement( 'hr' )
            ] );
        }
} );

Note that in this example we create two elements, which is not possible by using the previously mentioned elementToElement() helper.

For editor users, the best way to interact with complex structures is to act as independent entities and stay intact, for instance, when copied, pasted, and edited. CKEditor 5 allows that through the widget API. If you want to learn how to use it on top of elementToStructure(), be sure to check out the Implementing a block widget tutorial.

# Handling model element’s children

The example above uses an empty model element. If your model element may contain children, you need to specify where in the view these children should be placed. To do that, use the writer.createSlot() helper.

editor.conversion
    .for( 'downcast' )
    .elementToStructure( {
        model: 'wrappedParagraph',
        view: ( modelElement, conversionApi ) => {
            const { writer } = conversionApi;
            const paragraphViewElement = writer.createContainerElement( 'p', {}, [
                writer.createSlot()
            ] );

            return writer.createContainerElement( 'div', { class: 'wrapper' }, [
                paragraphViewElement
            ] );
        }
    } );

For editor users, the best way to interact with complex structures is to act as independent entities and stay intact, for instance, when copied, pasted, and edited. CKEditor 5 allows that through the widget API. If you want to learn how to use it on top of elementToStructure(), be sure to check out the Implementing a block widget tutorial.

# Attribute to element conversion helper

The attribute to element conversion is used to create formatting view elements like <b> or <span style="font-family: ..."> (that we call attribute elements). In this case, we do not convert a model element but a text node’s attribute. It is important to note that text formatting, such as bold or font size, should be represented in the model as text node attributes.

In general, the model does not implement a concept of “inline elements” (in the sense in which they are defined by CSS). The only scenarios in which inline elements can be used, are self-contained objects such as soft breaks (<br>) or inline images.

# Basic text attribute to model conversion

editor.conversion
    .for( 'downcast' )
    .attributeToElement( {
        model: 'bold',
        view: 'strong'
    } );

A model text node "CKEditor&nbsp;5" with a bold attribute will become a <strong>"CKEditor&nbsp;5"</strong> in the view.

# Using view element definition

You might want to output a view element that has more attributes, like a class name. To achieve that, you can provide an element definition in the view property:

editor.conversion
    .for( 'downcast' )
    .attributeToElement( {
        model: 'invert',
        view: {
            name: 'span',
            classes: [ 'font-light', 'bg-dark' ]
        }
    } );

Check out the ElementDefinition documentation for more details.

# Creating a view element using a callback

You can also generate the view element by a callback. This method is useful when the view element depends on the value of the model attribute.

editor.conversion
    .for( 'downcast' )
    .attributeToElement( {
        model: 'bold',
        view: ( modelAttributeValue, conversionApi ) => {
            const { writer } = conversionApi;

            return writer.createAttributeElement( 'span', {
                style: 'font-weight:' + modelAttributeValue
            } );
        }
    } );

The second parameter of the view callback is the DowncastConversionApi object. It contains many properties and methods that can be useful when writing more complex converters.

# Changing converter priority

In case there are other converters already present, you can prioritize your converter to override the existing ones. To do that, use the converterPriority property:

editor.conversion
    .for( 'downcast' )
    .attributeToElement( {
        model: 'bold',
        view: 'strong'
    } );

editor.conversion
    .for( 'downcast' )
    .attributeToElement( {
        model: 'bold',
        view: 'b',
        converterPriority: 'high'
    } );

In the example above, the first converter has no explicitly set priority hence it assumes default priority, which is normal. The second one overrides it by setting the priority to high. Using both of these converters at once will result in the bold attribute being converted to a <b> element.

# Attribute to attribute conversion helper

The attributeToAttribute() helper allows registering a converter that handles a specific attribute and converts it to an attribute of a view element.

Usually, when registering converters for elements (for example, by using elementToElement() or elementToStructure()), you will want to handle their attributes while handling the element itself.

The attributeToAttribute() helper comes in handy when for some reason you cannot cover a specific attribute inside the elementToElement() helper. For instance, when you are extending someone else’s plugin.

This type of converter helper only works if there is already an element converter provided. Trying to convert to an attribute while there is no receiving view element will cause an error.

# Basic attribute to attribute conversion

This conversion results in adding an attribute to a view node, basing on an attribute from a model node. For example, <imageInline src='foo.jpg'></imageInline> is converted to <img src='foo.jpg'></img>.

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: 'source',
        view: 'src'
    } );

# Converting specific model element and attribute

The converter in the example above will convert all the source model attributes in the document. You can limit its scope by providing the model element name. You achieve it with the following code:

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: {
            name: 'imageInline',
            key: 'source'
        },
        view: 'src'
    } );

This updated converter will only convert the source model attributes present on the imageInline model element.

# Creating a custom view element from a selected list of model values

Once you provide the array in the model.values property, the view property is expected to be an object with keys matching these values. This is best explained using the example below:

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: {
            name: 'styled',
            values: [ 'dark', 'light' ]
        },
        view: {
            dark: {
                key: 'class',
                value: [ 'styled', 'styled-dark' ]
            },
            light: {
                key: 'class',
                value: [ 'styled', 'styled-light' ]
            }
        }
    } );

# Creating a view attribute with a custom value based on the model value

The value of the view attribute can be modified in the converter. Below is a simple mapper that sets the class attribute based on the model attribute value:

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: 'styled',
        view: modelAttributeValue => ( {
            key: 'class',
            value: 'styled-' + modelAttributeValue
        } )
    } );

It is worth noting that providing a style property in this manner requires the returned value to be an object:

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: 'lineHeight',
        view: modelAttributeValue => ( {
            key: 'style',
            value: {
                'line-height': modelAttributeValue,
                'border-bottom': '1px dotted #ba2'
            }
        } )
    } );

# Changing converter priority

You can override the existing converters by specifying higher priority, like in the example below:

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: 'source',
        view: 'href'
    } );

editor.conversion
    .for( 'downcast' )
    .attributeToAttribute( {
        model: 'source',
        view: 'src',
        converterPriority: 'high'
    } );

First converter has the default priority, normal. The second converter will be called earlier because of its higher priority, thus the source model attribute will get converted to src view attribute instead of href.

# Further reading

Check out the dedicated guide with a full list of complementary upcast conversion helpers.