Kempo UI Icon Kempo UI

Kempo UI

Kempo UI Icon
Base Components
Utils

HtmlEditor

Table of Contents

A WYSIWYG HTML editor component with support for visual and code editing modes, form integration, and extensive formatting capabilities.

Complete Editor Example

A fully-featured editor with all controls. Copy and paste this example to get started quickly:

<k-html-editor name='content'>
<!-- Text formatting -->
<k-control-group slot='toolbar-top-left'>
<kc-bold></kc-bold>
<kc-italic></kc-italic>
<kc-underline></kc-underline>
<kc-strikethrough></kc-strikethrough>
</k-control-group>
<kc-inline-code slot='toolbar-top-left'></kc-inline-code>
<kc-menu slot='toolbar-top-left'>
<k-icon slot='icon' src='...'></k-icon>
<span slot='label'>Text Style</span>
<kc-format-block tag='p'>Paragraph</kc-format-block>
<kc-format-block tag='h1'>Heading 1</kc-format-block>
<kc-format-block tag='h2'>Heading 2</kc-format-block>
<kc-format-block tag='h3'>Heading 3</kc-format-block>
<kc-code-block></kc-code-block>
</kc-menu>
<!-- Lists, alignment, colors, link, table -->
<k-control-group slot='toolbar-top-left'>
<kc-bullet-list></kc-bullet-list>
<kc-number-list></kc-number-list>
</k-control-group>
<k-control-group slot='toolbar-top-left'>
<kc-align-left></kc-align-left>
<kc-align-center></kc-align-center>
<kc-align-right></kc-align-right>
<kc-align-justify></kc-align-justify>
</k-control-group>
<k-control-group slot='toolbar-top-left'>
<kc-text-color></kc-text-color>
<kc-text-background-color></kc-text-background-color>
</k-control-group>
<kc-clear-formatting slot='toolbar-top-left'></kc-clear-formatting>
<k-control-group slot='toolbar-top-left'>
<kc-create-link></kc-create-link>
<kc-insert-table></kc-insert-table>
</k-control-group>
<!-- Mode toggle + format code -->
<kc-mode slot='toolbar-top-right'></kc-mode>
<kc-format-code slot='toolbar-top-right'></kc-format-code>
<kc-fullscreen slot='toolbar-top-right'></kc-fullscreen>
<!-- Bottom toolbar stats -->
<kc-word-count slot='toolbar-bottom-left'></kc-word-count>
<kc-character-count slot='toolbar-bottom-left'></kc-character-count>
</k-html-editor>
Text Style Paragraph Heading 1 Heading 2 Heading 3 Code Block

Incompatible Content Example

When the editor's value contains HTML that Lexical does not support (such as <svg>, <script>, <style>, <video>, etc.), it automatically defaults to code mode to preserve the content. If the user attempts to switch to visual mode, a confirmation dialog warns that the incompatible code will be lost.

<k-html-editor id=svgEditor value="...">
<kc-mode slot='toolbar-top-right'></kc-mode>
</k-html-editor>

Custom Nodes Example

Custom nodes extend what Lexical understands in visual mode. Import a node file and list its name in the nodes attribute (comma-separated filenames without .js). The built-in HtmlComment node preserves HTML comments as inline visual chips rather than forcing code mode — comments round-trip through the editor without being stripped.

<!-- Import the HtmlComment node -->
<script type='module' src='src/components/htmlEditorNodes/HtmlComment.js'></script>
<!-- Register the node with nodes attribute -->
<k-html-editor nodes='HtmlComment' value='<p>This paragraph has an <!-- important note --> inline comment.</p>'>
<kc-mode slot='toolbar-top-right'></kc-mode>
</k-html-editor>

Pre-Configured Editors

The controls attribute lets you opt into a built-in toolbar without writing any slot markup. Set it directly on <k-html-editor> to one of the four levels below.

The four toolbar slots — toolbar-top-left, toolbar-top-right, toolbar-bottom-left, toolbar-bottom-right — all have slot fallback content driven by the controls value. Providing your own child element for any of these slots replaces just that section while leaving the others intact.

controls="full"

Every available control: text formatting, inline code, text-style dropdown (p, h1–h3, blockquote, code block), lists, alignment (with justify), text and background colours, clear formatting, link, table insertion, mode toggle, and word/character count.

<k-html-editor controls="full"></k-html-editor>

controls="normal"

A balanced everyday toolbar: bold, italic, underline, strikethrough, inline code, text-style dropdown, lists, alignment, link, and mode toggle. Word count shown in the bottom bar.

<k-html-editor controls="normal"></k-html-editor>

controls="minimal"

Essential controls only: bold, italic, underline, bullet list, numbered list, and mode toggle. Ideal for simple content forms.

<k-html-editor controls="minimal"></k-html-editor>

All controls levels share the full JavaScript API — getValue(), setValue(), bold(), etc. You can still target any individual section by slotting a child into the relevant named slot.

Slots available for custom content: toolbar-top-left, toolbar-top-right, toolbar-bottom-left, toolbar-bottom-right.

Setting Height

The default height is 400px. Override it with an inline style or CSS class on the element:

<k-html-editor style='height: 500px'></k-html-editor>

Disabled / Read-only

The disabled attribute makes the editor non-interactive (Lexical is set to non-editable in visual mode, Monaco is set to readOnly in code mode), mutes the toolbar, fades the host, and excludes the field from form submission. The readonly attribute lets the user select and copy text and switch modes, but they can't edit; the value is still submitted with the form.

<k-html-editor disabled value="..."></k-html-editor>
<k-html-editor readonly value="..."></k-html-editor>

JavaScript API Example

Use the editor without controls and interact with it programmatically using custom buttons and the JavaScript API:

<!-- Custom toolbar -->
<div>
  <button onclick="myEditor.bold()">Bold</button>
  <button onclick="myEditor.italic()">Italic</button>
  <button onclick="myEditor.unorderedList()">List</button>
  <button onclick="myEditor.setTextColor('#ff0000')">Red</button>
  <button onclick="insertHTML()">Insert</button>
  <button onclick="getContent()">Get Value</button>
</div>

<!-- Editor without controls -->
<k-html-editor id="myEditor"></k-html-editor>

<script>
  const myEditor = document.getElementById('myEditor');
  
  // Set initial content
  myEditor.setValue('<p>Select text and use the buttons above!</p>');
  
  function insertHTML() {
    myEditor.insertHTML('<p><b>Inserted content!</b></p>');
  }
  
  function getContent() {
    alert(myEditor.getValue());
  }
</script>

JavaScript Reference

Constructor

new HtmlEditor()

Extends ShadowComponent which extends LitElement.

Properties

name: String

The name attribute for form submission.

value: String

The HTML content of the editor. Can be get or set via JavaScript or HTML attribute.

mode: String

Current editing mode. Either 'visual' (WYSIWYG) or 'code' (raw HTML). Default: 'visual'. If the initial value contains content that is incompatible with the visual editor (such as <svg>, <script>, <style>, etc.), the editor automatically switches to 'code' mode to preserve the content. Custom nodes can extend what is considered compatible by defining a static isVisualCompatible(domNode) method.

controls: String

Activates a built-in toolbar. Accepted values:

The built-in controls appear as slot fallback content in the six toolbar slots. Providing your own child with a matching slot name for any section replaces just that section's defaults.

nodes: String

Comma-separated list of custom Lexical node module filenames (without .js) to load from the htmlEditorNodes/ directory. Custom nodes can define two static hooks:

lexical-src: String

Base URL for loading Lexical modules on this specific editor instance. When set, this overrides both the global window.kempo.lexicalUrl config and the built-in CDN default. See Global Configuration for the full resolution order.

monaco-src: String

Base URL for loading the Monaco editor on this specific editor instance. When set, this overrides both the global window.kempo.monacoUrl config and the built-in CDN default. See Global Configuration for the full resolution order.

disabled: Boolean

When true, the editor is non-interactive (Lexical's setEditable(false) in visual mode, Monaco's readOnly in code mode), the toolbar is muted, the host fades to opacity: 0.6, and the field is excluded from form submission. Syncs to disabled attribute.

readonly: Boolean

When true, the user can select and copy text but not edit. The value is still submitted with the form. Syncs to readonly attribute.

required: Boolean

When true and the editor is empty (no non-whitespace text), the element reports a valueMissing validity error to its parent <form>. Syncs to required attribute.

Global Configuration

The editor resolves dependency URLs in the following priority order (highest to lowest):

  1. Element attributelexical-src / monaco-src on the element itself
  2. Global configwindow.kempo.lexicalUrl / window.kempo.monacoUrl
  3. CDN fallbackhttps://esm.sh (Lexical) and https://cdn.jsdelivr.net/npm/monaco-editor@…/min (Monaco)

Set the global config before any <k-html-editor> initialises to apply a custom URL site-wide. This is useful when you have installed the packages locally via npm install lexical or npm install monaco-editor and serve them yourself:

<script>
  window.kempo = window.kempo || {};
  // Point to your locally served Lexical modules (must be an esm.sh-compatible base URL)
  window.kempo.lexicalUrl = '/node_modules';
  // Point to your locally served Monaco build
  window.kempo.monacoUrl = '/node_modules/monaco-editor/min';
</script>

The per-element attributes take precedence over the global config, so you can mix strategies — e.g., use a local Lexical build globally while one specific editor still points to a CDN via its lexical-src attribute.

Methods

Mode Control

setMode(mode): this

Set the editing mode. Accepts 'visual' or 'code'. If switching to 'visual' and the current content is incompatible, a confirmation dialog warns that incompatible code will be lost.

toggleMode(): this

Toggle between visual and code modes. Same incompatibility warning applies when switching to visual mode.

Content Management

getValue(): String

Returns the current HTML content.

setValue(html): this

Sets the HTML content of the editor. If the editor is in visual mode and the new content is incompatible, automatically switches to code mode.

clear(): this

Clears all content from the editor.

Text Formatting

bold(): this

Applies bold formatting to the selected text.

italic(): this

Applies italic formatting to the selected text.

underline(): this

Applies underline formatting to the selected text.

strikethrough(): this

Applies strikethrough formatting to the selected text.

Lists

orderedList(): this

Creates or toggles an ordered (numbered) list.

unorderedList(): this

Creates or toggles an unordered (bulleted) list.

Text Alignment

alignLeft(): this

Aligns text to the left.

alignCenter(): this

Centers the text.

alignRight(): this

Aligns text to the right.

alignJustify(): this

Justifies the text.

Text Color

setTextColor(color): this

Sets the foreground color of selected text. Accepts any valid CSS color value (hex, rgb, color name).

removeTextColor(): this

Removes foreground color from selected text.

setTextBackgroundColor(color): this

Sets the background color of selected text. Accepts any valid CSS color value (hex, rgb, color name).

removeTextBackgroundColor(): this

Removes background color from selected text.

Formatting Control

removeFormat(): this

Removes all formatting from the selected text.

formatBlock(tag): this

Formats the current block with the specified HTML tag (e.g., 'h1', 'h2', 'h3', 'p', 'pre').

insertHTML(html): this

Inserts HTML at the current cursor position.

insertAtCursor(html): this

Alias for insertHTML().

insertTable(rows, columns, includeHeaders, cellData): this

Inserts an HTML table at the cursor position.

insertElementAtCursor(element, selectAfter): this

Inserts a DOM element at the cursor position or replaces selection.

replaceSelectionWithElement(element, selectAfter): this

Replaces the current selection with a DOM element.

wrapSelection(before, after, savedSelection): this

Wraps the selected text with HTML tags. Handles complex cases like unwrapping existing tags and preventing nested pre tags.

Selection Management

getSelection(): Object | null

Returns an object containing information about the current selection: { text, html, range, selection }

getSelectedText(): String

Returns the plain text of the current selection.

getSelectedHTML(): String

Returns the HTML of the current selection.

setSelection(startNode, startOffset, endNode, endOffset): this

Programmatically sets the selection range.

selectAll(): this

Selects all content in the editor.

replaceSelection(html): this

Replaces the current selection with the provided HTML.

deleteSelection(): this

Deletes the currently selected content.

getValueWithSelectionMarkers(): Object

Returns the editor content with special Unicode markers indicating cursor position or selection boundaries. Useful for preserving selection state during content transformations.

Returns object with properties:

setValueFromSelectionMarkers(html, markers): this

Sets editor content and restores cursor/selection from Unicode markers. Companion method to getValueWithSelectionMarkers().

Links and Media

createLink(url): this

Creates a hyperlink from the selected text.

unlink(): this

Removes hyperlink from the selected text.

insertImage(url): this

Inserts an image at the cursor position.

History

undo(): this

Undoes the last action. Works in both visual mode (Lexical) and code mode (Monaco).

redo(): this

Redoes the last undone action. Works in both visual mode (Lexical) and code mode (Monaco).

Code Editor Controls

These methods control the Monaco code editor and are available in code mode. When used with controls="full", the toolbar exposes buttons for these automatically.

copyToClipboard(): this

Copies the full editor content to the clipboard.

setEditorTheme(theme): this

Sets the Monaco editor theme. Accepts 'auto', 'light', or 'dark'.

openFind(): this

Opens the Monaco Find & Replace widget.

foldAll(): this

Folds all collapsible regions in the code editor.

unfoldAll(): this

Unfolds all collapsed regions in the code editor.

increaseFontSize(): this

Increases the code editor font size by 2px (max 40px).

decreaseFontSize(): this

Decreases the code editor font size by 2px (min 8px).

setWordWrap(enabled): this

Enables or disables word wrapping in the code editor.

setMinimap(enabled): this

Shows or hides the minimap in the code editor.

resolveMonacoTheme(): String

Returns the resolved Monaco theme string ('vs' or 'vs-dark') based on the current editorTheme setting.

Events

change

Dispatched when the content changes. Event detail: { value: String }

mode-changed

Dispatched when the editing mode changes between 'visual' and 'code'. Event detail: { mode: String }