Editor Scripts (editor.js)
Components can ship an optional editor.js file that runs only inside the CMS page editor. It controls the editorial experience of a component — for example, which rich-text formatting controls an author sees — without changing what is published.
Editor-only. editor.js is never bundled into or served from a published site. Published pages receive only component.html and component.css. Because the script never reaches visitors, it is not HTML-sanitized and may contain arbitrary editor logic.
When to use it
Use editor.js when a field needs a richer authoring control than the default input, or when you want to constrain/expand the formatting available to authors. The primary use today is configuring the built-in rich-text editor toolbar.
File location
my-component/
├── details.json (required)
├── dialog.json (required)
├── component.html (required)
├── component.css (required)
├── component.js (optional) — data fetching at publish time
├── editor.js (optional) — EDITOR-ONLY editorial controls
└── variables.json (optional)
Contract
editor.js must export default a factory function. The editor calls it with a read-only cms API object and expects an editor-configuration object in return:
export default function registerComponentEditor(cms) {
return {
fields: {
// Keyed by the field `name` from dialog.json
content: {
editor: 'richtext',
toolbar: { /* … */ },
},
},
};
}
config.fields[name]
Property | Type | Description |
|---|---|---|
editor | string | The in-editor control to use. Currently "richtext". |
toolbar | object | Rich-text toolbar configuration (see below). |
allowedTags | string[] | HTML tags the editor may keep in the stored value. |
placeholder | string | Placeholder text shown when the field is empty. |
Rich-text toolbar
Property | Type | Controls |
|---|---|---|
paragraphStyles | { label, value, className? }[] | Block style dropdown. value is the block tag (p, h1…h6, pre for a code block). Optional className keeps value as p and applies a CSS class to the block for site theming. |
inline | string[] | bold, italic, underline, strikethrough, code (inline <code>), mark (highlight), subscript, superscript. |
blocks | string[] | unorderedList, orderedList, blockquote, horizontalRule. |
align | string[] | left, center, right, justify — align the containing block. |
insert | string[] | link, unlink. |
utilities | string[] | removeFormat. |
Brand text styles
Paragraph-style options can carry an optional className. When picked, the selection is wrapped in a plain <p> and the class is applied to that block. The built-in Rich Text component uses this to expose four unstyled brand styles — cms-rt-brand1 through cms-rt-brand4 — that site CSS can theme however it likes:
paragraphStyles: [
{ label: 'Paragraph', value: 'p' },
{ label: 'Heading 1', value: 'h1' },
// …
{ label: 'Brand style 1', value: 'p', className: 'cms-rt-brand1' },
{ label: 'Brand style 2', value: 'p', className: 'cms-rt-brand2' },
{ label: 'Brand style 3', value: 'p', className: 'cms-rt-brand3' },
{ label: 'Brand style 4', value: 'p', className: 'cms-rt-brand4' },
],
Only one brand class is applied to a block at a time; choosing a different brand style replaces the previous one.
Full rich-text example
The built-in Rich Text component ships this editor.js to enable full editing — paragraph styles (Heading 1–6, code block), bold/italic/underline/strikethrough, inline code/highlight/subscript/superscript, links, lists, blockquotes and horizontal rules:
export default function registerComponentEditor(cms) {
void cms;
return {
fields: {
content: {
editor: 'richtext',
toolbar: {
paragraphStyles: [
{ label: 'Paragraph', value: 'p' },
{ label: 'Heading 1', value: 'h1' },
{ label: 'Heading 2', value: 'h2' },
{ label: 'Heading 3', value: 'h3' },
{ label: 'Heading 4', value: 'h4' },
{ label: 'Heading 5', value: 'h5' },
{ label: 'Heading 6', value: 'h6' },
{ label: 'Code block', value: 'pre' },
{ label: 'Brand style 1', value: 'p', className: 'cms-rt-brand1' },
{ label: 'Brand style 2', value: 'p', className: 'cms-rt-brand2' },
{ label: 'Brand style 3', value: 'p', className: 'cms-rt-brand3' },
{ label: 'Brand style 4', value: 'p', className: 'cms-rt-brand4' },
],
inline: ['bold', 'italic', 'underline', 'strikethrough', 'code', 'mark', 'subscript', 'superscript'],
blocks: ['unorderedList', 'orderedList', 'blockquote', 'horizontalRule'],
align: ['left', 'center', 'right'],
insert: ['link', 'unlink'],
utilities: ['removeFormat'],
},
allowedTags: [
'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'strong', 'em', 'u', 's', 'a',
'ul', 'ol', 'li', 'blockquote', 'hr',
'pre', 'code', 'mark', 'sub', 'sup',
],
placeholder: 'Start typing your content…',
},
},
};
}
Defaults & fallbacks
- If a richtext field has no editor.js configuration, the editor uses a full default rich-text toolbar automatically.
- If editor.js is malformed, the editor logs a warning and falls back to the default control — a bad editor script can never break the editing UI.
Constraints
- Must export default a factory function (or a plain config object).
- Field keys in config.fields must match name values in dialog.json.
- Runs only in the editor; do not rely on it for published output.
- Maximum size: 512 KB (like all component files).