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).

Related topics