Rich text editor customization

The rich text editor in Xperience can be customized to meet specific requirements, with the ability to offer different features in different contexts. For example, you can limit the set of buttons and offered formatting options, or change the editor’s behavior and appearance.

Customization options

The functionality of an editor instance is determined by its configuration. The system uses several built-in configurations for different contexts in the administration UI. You can create custom configurations and assign them when using the rich text editor. You can also control which rich text editor configurations are loaded by the system, and make dynamic adjustments of the configuration content, enabling you to customize the editor throughout the system.

The rich text editor is based on the Froala WYSIWYG editor. To customize the editor, you can adjust a wide range of configurable Froala options in the rich text editor configurations.

To go beyond the built-in Froala functionality, add plugins to the editor. You can use existing default Xperience plugins and community plugins, or develop your own custom plugins.

If you configure custom handlers for the rich text editor drop event or disable the upload functionality using Froala options (e.g., imageUpload), mass asset upload may not work correctly.

Create and use custom editor configurations

Use the following process to create and assign a custom configuration:

  1. Define editor configurations
  2. Register editor configurations
  3. Assign the configurations to:
    1. rich text editing components
    2. fields that use the rich text form component

Define editor configurations

Some configurations may need additional custom CSS to work correctly. For more information on how to add custom styles to the rich text editor, see Add custom CSS to the rich text editor.

Editor configurations are defined using JSON options objects that the system uses when instantiating a rich text editor.

  1. Prepare a custom assembly (Class Library project) in your Xperience solution.

    • We recommend placing code and resources used only by the Xperience administration in dedicated assemblies.
  2. Create a new JSON file in the assembly (place the file into a suitable directory for storing editor configurations).

  3. In this file, define an editor configuration using the available configuration options.

    The following example includes only basic text formatting options in the editor toolbar, along with the default Xperience plugins for adding media files and links.

    JSON
    CustomEditorConfiguration.json
    
    
     {
       "imageDefaultWidth": 0,
       "imageResize": true,
       "imageUpload": false,
       "imagePaste": false,
       "imageDefaultMargin": null,
       "imageMove": true,
       "imageAddNewLine": false,
       "toolbarButtons": [
         "paragraphFormat",
         "|",
         "bold",
         "italic",
         "underline",
         "|",
         "align",
         "formatOL",
         "formatUL",
         "|",
         "insertAsset",
         "insertLinkDropDown"
       ],
       "imageEditButtons": [ "imageReplace", "imageAlt", "imageAlign", "imageDisplay", "imageSize", "imageRemove" ],
       "customPlugins": [
         {
           "pluginName": "@kentico/xperience-admin-base/Asset"
         },
         {
           "pluginName": "@kentico/xperience-admin-base/Link",
           "pluginOptions": {
             "dropdownOptions": [
               {
                 "linkOption": "asset",
                 "componentName": "@kentico/xperience-admin-base/AssetPanel"
               },
               {
                 "linkOption": "external"
               }
             ]
           }
         }
       ]
     }
    
     

Froala plugins

The Froala editor uses a modular architecture that in its default configuration contains only essential features. You can add existing community plugins or implement custom plugins to expand the available feature set.

Register editor configurations

Editor configurations must fulfill both of the following:

  • Must be stored as embedded resources within application assemblies.
  • Must be registered in the system under a unique identifier (the registration code must be placed in a custom assembly with class discovery enabled).

Use the following process to register a configuration:

  1. Embed the configuration JSON in an assembly via the EmbeddedResource element.

    XML
    Embedding the configuration via an assembly .csproj file
    
    
     <Project Sdk="Microsoft.NET.Sdk">
    
     ...
    
         <ItemGroup>
                 <EmbeddedResource Include="relative\path\to\custom\configuration.json" />
         </ItemGroup>
    
     </Project>
    
     
  2. Create a new class that inherits from RichTextEditorConfiguration. Within the class:

    1. Assign a unique string identifier to the configuration.

      • Use a constant named IDENTIFIER. This allows you to easily reference the configuration in your code.
      • We recommend using a unique prefix in your identifiers to prevent conflicts when deploying components to other projects. For example, use prefixes matching your company name.
    2. Call the base class constructor with the following parameters:

      • relativePath – the relative path to the configuration file within the assembly.
      • (Optional) baseNamespace – allows you to provide a different assembly name in case the configuration file is stored in a different assembly. If set, the system searches the specified assembly (using the given relativePath) instead.
    C#
    Example - registering a configuration to the system
    
    
     using Kentico.Xperience.Admin.Base.Forms;
    
     public class CustomRichTextEditorConfiguration : RichTextEditorConfiguration
     {
         // Relative path to the configuration file within the specified assembly
         // This example configuration is located under: <AssemblyName>\Misc\RichTextEditorConfiguration\
         private const string CONFIGURATION_PATH = "Misc\\RichTextEditorConfiguration\\RichTextEditorConfiguration.json";
    
         // String identifier assigned to the configuration
         public const string IDENTIFIER = "Acme.Web.EditorConfiguration";
    
         // User-friendly name of the configuration
         public const string DISPLAY_NAME = "Custom ACME editor configuration";
    
         public CustomRichTextEditorConfiguration()
             : base(CONFIGURATION_PATH)
         {
         }
     }
    
     
  3. Register the class using the RegisterRichTextEditorConfiguration assembly attribute. Provide the following arguments for the registration attribute:

    • the configuration’s identifier

    • the type of the class derived from RichTextEditorConfiguration (using the typeof operator)

    • a user-friendly display name, which appears when selecting the configuration for the Rich text editor form component in the field editor.

      C#
      Example - registering a configuration to the system
      
      
        using Kentico.Xperience.Admin.Base.Forms;
      
        [assembly: RegisterRichTextEditorConfiguration(CustomRichTextEditorConfiguration.IDENTIFIER, typeof(CustomRichTextEditorConfiguration), CustomRichTextEditorConfiguration.DISPLAY_NAME)]
        public class CustomRichTextEditorConfiguration : RichTextEditorConfiguration
      
        

The configuration is now registered in the system and can be assigned to:

Assign editor configurations to rich text editing components

To assign custom configurations to rich text editors within component configuration dialogs:

  1. Register the configuration.
  2. Decorate the component property intended to store the rich text content with the RichTextEditorComponent attribute. The attribute assigns the Rich text editor form component as the property’s editing component.
  3. Provide the configuration to use via the attribute’s ConfigurationName property. You can use the declared IDENTIFIER constant to reference the desired configuration.
C#
Example - assigning a custom toolbar to a configuration property


using Kentico.Forms.Web.Mvc;
using Kentico.Xperience.Admin.Base.FormAnnotations;

public class TitledSectionProperties : IFormSectionProperties
{
    // Uses the 'RichTextComponent' as the editing component of the 'SectionText' property
    // and assigns a custom configuration for the Froala editor
    [RichTextEditorComponent(ConfigurationName = CustomRichTextEditorConfiguration.IDENTIFIER)]
    public string SectionText { get; set; }
}

Assign editor configurations to rich text fields

For fields that use the Rich text editor form component, you can set custom configurations via the Configuration name property.

  1. Register the configuration in the system.
  2. Open the object you wish to modify in the field editor.
  3. Select a field using the Rich text editor form component (available for fields of the Rich text (HTML) data type).
  4. Select the configuration via the Configuration name property.
  5. Save your changes.

The modified field now uses the specified configuration.

System rich text editor configurations

The rich text editors in Xperience use different configurations tailored to different contexts in the admin UI. The system has one default configuration and several dedicated configurations which extend or override the default options with additional configuration. Access the constants in the RichTextEditorConfigurationsIdentifiers class to reference these configurations:

  • STRUCTURED_CONTENT – for reusable content items
  • WEBSITE_STRUCTURED_CONTENT – for pages
  • HEADLESS_STRUCTURED_CONTENT – for headless items
  • AUTORESPONDER_EMAIL_CONTENT – for emails with the Form autoresponder purpose
  • EMAIL_CONTENT – for all other emails
  • NOTIFICATION_EMAIL_CONTENT – for notifications
  • CONSENT_TEXT – for consent text fields
  • CONTACT_NOTES – for the Notes field when editing contact details
  • DEFAULT – used when no configuration is assigned outside of the above location (i.e., the configuration name is null)

You can change what configurations are loaded by default and use your own custom configurations instead.

Default editor configuration

When added in its default state to custom fields or editing component properties without specifying a configuration, the editor uses the following configuration:

JSON


{
    attribution: false,
    listAdvancedTypes: false,
    linkEditButtons: ['linkOpen', 'linkEdit', 'linkRemove'],
    linkInsertButtons: ['linkBack'],
    linkConvertEmailAddress: false,
    quickInsertEnabled: false,
    pasteDeniedAttrs: ['id', 'style'],
    toolbarButtons: [
        'bold',
        'italic',
        'underline',
        'paragraphFormat',
        'formatOL',
        'formatUL',
        'outdent',
        'indent',
        'alignLeft',
        'alignCenter',
        'alignRight',
        'html',
        'undo',
        'redo'
    ],
    paragraphFormat: {
        N: 'Paragraph',
        H1: 'Heading 1',
        H2: 'Heading 2',
        H3: 'Heading 3',
        H4: 'Heading 4',
        H5: 'Heading 5',
        H6: 'Heading 6',
    },
    customPlugins: []
}

Configuration example for a page with structured content

For example, the following configuration is applied by the system when editing structured content of pages on the Content view under a website channel:

JSON
WEBSITE_STRUCTURED_CONTENT configuration


{
  "imageDefaultWidth": 0,
  "imageResize": true,
  "imageUpload": false,
  "imagePaste": false,
  "imageDefaultMargin": null,
  "imageMove": true,
  "imageAddNewLine": false,
  "toolbarButtons": [
    "bold",
    "italic",
    "underline",
    "paragraphFormat",
    "formatOL",
    "formatUL",
    "outdent",
    "indent",
    "alignLeft",
    "alignCenter",
    "alignRight",
    "html",
    "insertLinkDropDown",
    "undo",
    "redo"
  ],
  "imageEditButtons": [ "imageReplace", "imageAlt", "imageAlign", "imageDisplay", "imageSize", "imageRemove" ],
  "customPlugins": [
    {
      "pluginName": "@kentico/xperience-admin-base/Link",
      "pluginOptions": {
        "dropdownOptions": [
          {
            "linkOption": "asset",
            "componentName": "@kentico/xperience-admin-base/AssetPanel"
          },
          {
            "linkOption": "external"
          },
          {
            "linkOption": "webpage",
            "componentName": "@kentico/xperience-admin-websites/PageLink"
          }
        ]
      }
    }
  ]
}

Override and extend editor configurations dynamically

The system allows developers to exactly control which rich text editor configurations are loaded, and to make dynamic adjustments of the configuration content. This can be done by providing a custom implementation of the IRichTextEditorConfigurationProvider service. In most cases, we recommend extending the default implementation via the decorator pattern.

See the following sections for examples of scenarios that use this customization pattern:

Replace editor configurations loaded by default

When adding fields with the Rich text editor form component in the field editor, the selected Configuration name determines which options and plugins are available in the editor toolbar. If the user leaves the Configuration name empty, the field uses a default editor configuration. The default configuration depends on the admin UI location where the field will be used.

You can use the IRichTextEditorConfigurationProvider service to replace the configuration loaded by default with a custom editor configuration that is preferred for your project. With this change, users no longer need to select your custom configuration for every rich text field.

  1. Open your Xperience solution in Visual Studio.
  2. Add a custom assembly (Class Library project) with class discovery enabled to the solution, or re-use an existing assembly.
  3. Create a new custom class in the assembly, and implement the IRichTextEditorConfigurationProvider interface.
  4. Add the GetConfigurationByName method:
    • The configurationName parameter contains the identifier of the configuration that was requested. Write a condition to detect the configurations that you want to replace.
      • Access the constants in the RichTextEditorConfigurationsIdentifiers class to get the identifiers of the system’s configurations.
      • If no configuration is assigned to the rich text editor fields outside of the specified locations, the configurationName is null and they the have the DEFAULT “empty” configuration.
    • Use the decorator customization pattern to call the default implementation for cases where you do not wish to replace the requested configuration.
  5. Register the implementation using the RegisterImplementation assembly attribute.

The following example shows a custom implementation of IRichTextEditorConfigurationProvider. The implementation changes the configuration loaded for page fields on the Content view.

C#
Replacing the default rich text editor configuration for page fields

using System;

using CMS;

using Kentico.Xperience.Admin.Base.Forms;
using Kentico.Xperience.Admin.Websites;

// Registers the custom IRichTextEditorConfigurationProvider implementation
[assembly: RegisterImplementation(typeof(IRichTextEditorConfigurationProvider), typeof(CustomDefaultRichTextEditorConfigurationProvider))]

public class CustomDefaultRichTextEditorConfigurationProvider : IRichTextEditorConfigurationProvider
{
    private readonly IRichTextEditorConfigurationProvider richTextEditorConfigurationProvider;
    
    public CustomDefaultRichTextEditorConfigurationProvider(IRichTextEditorConfigurationProvider richTextEditorConfigurationProvider)
    {
        // Gets an instance of the default IRichTextEditorConfigurationProvider service
        this.richTextEditorConfigurationProvider = richTextEditorConfigurationProvider;
    }
    
    // Loads the configuration for instances of the rich text editor in the administration
    public string GetConfigurationByName(string configurationName)
    {
        // The 'configurationName' parameter contains the identifier of the requested editor configuration
        // This condition replaces the system's default editor configuration for page fields
        if (string.Equals(configurationName, RichTextEditorConfigurationsIdentifiers.WEBSITE_STRUCTURED_CONTENT, StringComparison.OrdinalIgnoreCase))
        {
            // Use the identifier of your preferred custom configuration
            // For example, access the IDENTIFIER constant of the class used to register the configuration
            return richTextEditorConfigurationProvider.GetConfigurationByName(CustomDefaultEditorConfiguration.IDENTIFIER);
        }

        // Calls the default implementation and returns the unmodified configuration in all other cases
        return richTextEditorConfigurationProvider.GetConfigurationByName(configurationName);
    }
}

Add custom CSS to the rich text editor

The following example demonstrates how to adjust the appearance and design of the rich text editor by inserting custom styles. The recommended approach is to create a custom plugin containing the required elements and CSS, and then use the IRichTextEditorConfigurationProvider service to dynamically insert your plugin into the appropriate editor configurations.

  1. Create a custom JavaScript module for the admin UI.
    • Use the client development boilerplate project to simplify the set up. Make sure the project has the Kentico.Xperience.Admin NuGet package installed so it can access admin UI-related APIs.
  2. Extend the module’s webpack.config.js to support CSS loading. For more information on configuring CSS loaders, see the webpack documentation.
    JS
    webpack.config.js
    
     const projectConfig = {
         module: {
             rules: [
                 ...
                 // Adds the loader for basic CSS
                 {
                     test: /\.css$/,
                     use: ["style-loader", "css-loader"],
                 },
             ],
         },
      ...
     };
     
  3. Within the module, create a React component file for the plugin.
    • Import any required styles. You do not need to return any custom markup.
    JS
    CustomCSSRichTextEditorPlugin.tsx
    
     import React from 'react';
    
     // Imports custom styles
     import './CustomCSSRichTextEditorPlugin.css';
    
     // The plugin component's name must have the 'RichTextEditorPlugin' suffix
     export const CustomCSSRichTextEditorPlugin = () => {
         return (
             // No markup to return
             <></>
         );
     };
     
  4. Add files containing the styles that you wish to import.
    • You can use basic CSS, or any CSS extension language supported by your module’s environment.
    CSS
    CustomCSSRichTextEditorPlugin.css
    
     /* Examples of custom styles for the fr-view class, which is applied to the editor's content area */
     .fr-view h2 {
         font-size: 3em;
     }
    
     .fr-view {
         background-color: red;
     }
     
  5. Export the plugin component from the module:
    TSX
    entry.tsx
    
     export * from './path/CustomCSSRichTextEditorPlugin';
     
  6. Create a class in the admin module, and implement the IRichTextEditorConfigurationProvider interface.
  7. Add the GetConfigurationByName method and insert the customPlugins array with your custom CSS plugin into every configuration:
    C#
    Adding the custom CSS plugin to rich text editor configurations
    
     using System.Text.Json;
     using System.Text.Json.Nodes;
    
     using CMS;
     using Kentico.Xperience.Admin.Base.Forms;
    
     // Registers the custom IRichTextEditorConfigurationProvider implementation
     [assembly: RegisterImplementation(typeof(IRichTextEditorConfigurationProvider), typeof(CustomCssRichTextEditorConfigurationProvider))]
    
     public class CustomCssRichTextEditorConfigurationProvider : IRichTextEditorConfigurationProvider
     {
         private readonly IRichTextEditorConfigurationProvider richTextEditorConfigurationProvider;
    
         public CustomCssRichTextEditorConfigurationProvider(IRichTextEditorConfigurationProvider richTextEditorConfigurationProvider)
         {
             // Gets an instance of the default IRichTextEditorConfigurationProvider service
             this.richTextEditorConfigurationProvider = richTextEditorConfigurationProvider;
         }
    
         public string GetConfigurationByName(string configurationName)
         {
             // Gets the requested rich text editor configuration
             var configuration = richTextEditorConfigurationProvider.GetConfigurationByName(configurationName);
    
             // Adds the custom CSS plugin to the configuration's JSON content
             var jsonObj = JsonNode.Parse(configuration);
             if (jsonObj?["customPlugins"] is JsonArray optionsArray)
             {
                 var newOption = new JsonObject
                 {
                     // Replace 'orgName' and 'projectName' with the values for your client admin module.
                     // The final part of the plugin name must match the name of 
                     // the plugin's React component without the 'RichTextEditorPlugin' suffix.
                     ["pluginName"] = "@orgName/projectName/CustomCSS"
                 };
    
                 optionsArray.Add(newOption);
             }
    
             // Returns the modified configuration content, including the custom CSS plugin
             return jsonObj?.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
         }
     }
     
    In the pluginName configuration value, replace orgName and projectName with your own values. By default, the boilerplate project uses acme and web-admin respectively. The final part of the plugin name must match the name of the plugin’s React component without the RichTextEditorPlugin suffix (CustomCSS in this example).

The custom IRichTextEditorConfigurationProvider service dynamically inserts the custom CSS plugin into every rich text editor configuration, and the imported CSS is applied to the editor in the admin UI.