Using an Editor Config Contributor Client Extension

Adding Custom CKEditor 5 Styles

Liferay DXP 2026.Q1+

With an Editor Config Contributor client extension, you can add your own entries to the CKEditor 5 Styles drop-down. A custom style has two moving parts. The style definition registers the entry with the editor, and the CSS gives the style its visible effect. This example uses a sample client extension that adds a Featured Content style for highlighting a paragraph. See Using an Editor Config Contributor Client Extension to learn more about this client extension type.

Note

CKEditor 5 is the default text editor in Liferay DXP 2026.Q2+. In Liferay DXP 2026.Q1, activate it with the release feature flag Enhanced Rich Text Editor (LPD-11235). See Upgrading to CKEditor 5 for the upgrade’s impact on existing content.

Prerequisites

To work with Editor Config Contributor client extensions, follow these steps:

  1. Install a supported version of Java.

    Note

    Check the compatibility matrix for supported JDKs, databases, and environments. See JVM Configuration for recommended JVM settings.

  2. Download and unzip the sample workspace:

    curl -o com.liferay.sample.workspace-latest.zip https://repository.liferay.com/nexus/service/local/artifact/maven/content\?r\=liferay-public-releases\&g\=com.liferay.workspace\&a\=com.liferay.sample.workspace\&\v\=LATEST\&p\=zip
    
    unzip com.liferay.sample.workspace-latest.zip
    

Now you can examine and modify the sample Editor Config Contributor client extensions.

Examine and Modify the Client Extension

The Editor Config Contributor example is in the sample workspace’s client-extensions/liferay-sample-editor-config-contributor-4/ folder. Its client-extension.yaml file defines these details:

assemble:
    -   from: build
        into: static
liferay-sample-editor-config-contributor-4:
    editorConfigKeys:
        -   sampleReactCKEditor5ClassicEditor
    name: Liferay Sample Editor Config Contributor CKEditor 5 Style
    type: editorConfigContributor
    url: index.js

The client extension declares its ID (liferay-sample-editor-config-contributor-4), its type (editorConfigContributor), and the editors it applies to (editorConfigKeys). Its url property points to index.js, the bundled JavaScript file the build produces. The assemble block packages the entire build/ folder into the client extension’s .zip file as static resources, including the index.js file. See the Editor Config Contributor YAML Configuration Reference for more information.

Important

The sample’s key (sampleReactCKEditor5ClassicEditor) uses one of Liferay’s sample editor applications, which aren’t part of a standard Liferay installation. To use the sample, update the key to the editor you want to configure.

To apply the extension to rich text fields in Liferay DXP, including the Content field for web content, add rich_text to the key list:

editorConfigKeys:
    -   rich_text
    -   sampleReactCKEditor5ClassicEditor

Examine the Style Definition and CSS

The transformer logic is in src/index.ts, which exports an editorConfigTransformer function that receives the editor’s configuration object and returns a transformed copy:

import {
	EditorConfigTransformer,
	EditorTransformer,
} from '@liferay/js-api/editor';

const STYLE_ELEMENT_ID = 'liferay-sample-editor-config-contributor-4-styles';

const editorConfigTransformer: EditorConfigTransformer<any> = (config) => {
	if (!document.getElementById(STYLE_ELEMENT_ID)) {
		const styleEl = document.createElement('style');

		styleEl.id = STYLE_ELEMENT_ID;
		styleEl.textContent = `
			.ck-content .featured-content {
				background-color: #e8f4fd;
				border-left: 4px solid #2196f3;
				border-radius: 0 4px 4px 0;
				padding: 8px 16px;
			}
		`;

		document.head.appendChild(styleEl);
	}

	const toolbar = config.toolbar as any;
	const existingItems: string[] = Array.isArray(toolbar)
		? toolbar
		: toolbar?.items ?? [];

	return {
		...config,
		style: {
			definitions: [
				...((config as any).style?.definitions ?? []),
				{
					classes: ['featured-content'],
					element: 'p',
					name: 'Featured Content',
				},
			],
		},
		toolbar: {
			items: existingItems.includes('style')
				? existingItems
				: [...existingItems, '|', 'style'],
		},
	};
};

const editorTransformer: EditorTransformer<any> = {
	editorConfigTransformer,
};

export default editorTransformer;

The transformer adds one entry to style.definitions: a style named Featured Content that applies the featured-content class to a paragraph element. The spread operator preserves the definitions already present, so the sample extends the list instead of replacing it. Once registered, Featured Content appears in the editor’s Styles drop-down.

The transformer also appends a separator ('|') and 'style' to the toolbar’s items when style isn’t already present. This ensures the Styles drop-down always appears in the toolbar. A toolbar configuration can appear in two shapes: a plain array of item names, or an object with an items array. The existingItems variable holds the item names from whichever shape is present, so the check for 'style' works in both cases.

A definition alone isn’t enough; it tells the editor which class to apply, but without CSS the style has no visible effect. To apply custom CSS in the editor view, the sample injects a <style> element into document.head. Before injecting, it checks for the liferay-sample-editor-config-contributor-4-styles ID, which keeps the element from being added more than once. Once applied, it adds a light blue background, a blue left border, and extra padding to featured-content paragraphs.

CKEditor 5 marks its editing area with the ck-content class. The prefix scopes the rule to content inside the editor, so featured-content elements elsewhere on the page aren’t affected.

Remember, this CSS only applies to the editor. To style featured-content on the published page, you must supply the same rule to your site using a CSS or Theme CSS client extension.

Note

The transformer touches only the configuration object and standard DOM APIs. Because of that, the extension needs no @ckeditor package. Its package.json declares one dependency, "@liferay/js-api": "0.8.0", and its build is a plain esbuild src/index.ts --outdir=build --bundle --format=esm with no externals. By contrast, Adding a Custom CKEditor 5 Plugin requires CKEditor type packages and a wildcard external.

Deploy the Client Extension

Start a new Liferay DXP instance by running

docker run -it -m 8g -p 8080:8080 liferay/dxp:2026.q1.9-lts

Sign in to Liferay at http://localhost:8080 using the email address test@liferay.com and the password test. When prompted, change the password to learn.

Once Liferay starts, open a new terminal and run this command from the client-extensions/liferay-sample-editor-config-contributor-4/ folder:

../../gradlew clean deploy -Ddeploy.docker.container.id=$(docker ps -lq)

This builds the extension and deploys the zip to Liferay’s deploy/ folder. For the full deploy flow, including Liferay SaaS deployment, see Using an Editor Config Contributor Client Extension.

Confirm deployment is successful in your Liferay instance’s console:

STARTED liferaysampleeditorconfigcontributor4_...

Verify the Style

After you deploy the extension with rich_text in its editorConfigKeys, apply the style in a web content article:

  1. Open the Site Menu (Site Menu), expand Content & Data, and click Web Content.

  2. Click New and select Basic Web Content.

  3. In the Content field, type a paragraph and highlight it.

  4. In the editor toolbar, open the Styles drop-down and select Featured Content.

    The paragraph takes the featured-content styling: a light blue background with a blue left border.

You’ve successfully added a custom entry to the CKEditor 5 Styles drop-down using an Editor Config Contributor client extension.