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:
- Define editor configurations
- Register editor configurations
- Assign the configurations to:
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.
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.
Create a new JSON file in the assembly (place the file into a suitable directory for storing editor configurations).
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.
JSONCustomEditorConfiguration.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:
Embed the configuration JSON in an assembly via the
EmbeddedResourceelement.XMLEmbedding the configuration via an assembly .csproj file<Project Sdk="Microsoft.NET.Sdk"> ... <ItemGroup> <EmbeddedResource Include="relative\path\to\custom\configuration.json" /> </ItemGroup> </Project>Create a new class that inherits from
RichTextEditorConfiguration. Within the class: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.
- Use a constant named
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 givenrelativePath) instead.
C#Example - registering a configuration to the systemusing 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) { } }Register the class using the
RegisterRichTextEditorConfigurationassembly attribute. Provide the following arguments for the registration attribute:the configuration’s identifier
the type of the class derived from
RichTextEditorConfiguration(using thetypeofoperator)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 systemusing 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:
- Register the configuration.
- Decorate the component property intended to store the rich text content with the
RichTextEditorComponentattribute. The attribute assigns the Rich text editor form component as the property’s editing component. - Provide the configuration to use via the attribute’s
ConfigurationNameproperty. You can use the declaredIDENTIFIERconstant to reference the desired configuration.
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.
- Register the configuration in the system.
- Open the object you wish to modify in the field editor.
- Select a field using the Rich text editor form component (available for fields of the Rich text (HTML) data type).
- Select the configuration via the Configuration name property.
- 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 itemsWEBSITE_STRUCTURED_CONTENT– for pagesHEADLESS_STRUCTURED_CONTENT– for headless itemsAUTORESPONDER_EMAIL_CONTENT– for emails with the Form autoresponder purposeEMAIL_CONTENT– for all other emailsNOTIFICATION_EMAIL_CONTENT– for notificationsCONSENT_TEXT– for consent text fieldsCONTACT_NOTES– for the Notes field when editing contact detailsDEFAULT– used when no configuration is assigned outside of the above location (i.e., the configuration name isnull)
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:
{
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:
{
"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 – Set the system to automatically use a custom configuration for fields on the page Content view. The custom configuration then does not have to be selected manually when creating page content types.
- Add custom CSS to the rich text editor – Adjust the rich text editor appearance across the system by inserting custom CSS to all system rich text editor configurations.
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.
- Open your Xperience solution in Visual Studio.
- Add a custom assembly (Class Library project) with class discovery enabled to the solution, or re-use an existing assembly.
- Create a new custom class in the assembly, and implement the
IRichTextEditorConfigurationProviderinterface. - Add the
GetConfigurationByNamemethod:- The
configurationNameparameter 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
RichTextEditorConfigurationsIdentifiersclass 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
configurationNameisnulland they the have theDEFAULT“empty” configuration.
- Access the constants in the
- Use the decorator customization pattern to call the default implementation for cases where you do not wish to replace the requested configuration.
- The
- Register the implementation using the
RegisterImplementationassembly attribute.
The following example shows a custom implementation of IRichTextEditorConfigurationProvider. The implementation changes the configuration loaded for page fields on the Content view.
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.
- 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.
- Extend the module’s
webpack.config.jsto support CSS loading. For more information on configuring CSS loaders, see the webpack documentation.JSwebpack.config.jsconst projectConfig = { module: { rules: [ ... // Adds the loader for basic CSS { test: /\.css$/, use: ["style-loader", "css-loader"], }, ], }, ... }; - Within the module, create a React component file for the plugin.
- Import any required styles. You do not need to return any custom markup.
JSCustomCSSRichTextEditorPlugin.tsximport 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 <></> ); }; - 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.
CSSCustomCSSRichTextEditorPlugin.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; } - Export the plugin component from the module:
TSXentry.tsx
export * from './path/CustomCSSRichTextEditorPlugin'; - Create a class in the admin module, and implement the
IRichTextEditorConfigurationProviderinterface. - Add the
GetConfigurationByNamemethod and insert thecustomPluginsarray with your custom CSS plugin into every configuration:C#Adding the custom CSS plugin to rich text editor configurationsusing 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 thepluginNameconfiguration value, replaceorgNameandprojectNamewith your own values. By default, the boilerplate project usesacmeandweb-adminrespectively. 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.