Skip to content

Code Block Lowlight

CodeBlockLowlight extends the built-in CodeBlock node with syntax highlighting powered by lowlight (a lightweight highlight.js wrapper). It adds language auto-detection, tab indentation with configurable tab size, and incremental re-highlighting that only updates affected code blocks on edits. It inherits all commands, keyboard shortcuts, toolbar items, and input rules from CodeBlock.

Separate package. Install alongside the core package.

Edit the JavaScript code below to see syntax highlighting update in real time. Try creating a new code block by typing ```css or ```python followed by a space. Use Tab to indent and Shift-Tab to outdent inside code blocks.

Click to try it out
Terminal window
pnpm add @domternal/extension-code-block-lowlight lowlight
import { Editor, Document, Paragraph, Text } from '@domternal/core';
import { CodeBlockLowlight } from '@domternal/extension-code-block-lowlight';
import { createLowlight, common } from 'lowlight';
import '@domternal/theme';
const lowlight = createLowlight(common);
const editor = new Editor({
element: document.getElementById('editor')!,
extensions: [
Document, Paragraph, Text,
CodeBlockLowlight.configure({ lowlight }),
],
});

createLowlight(common) registers ~40 common languages. Use createLowlight(all) for all ~190 languages, or register specific ones with lowlight.register().

CodeBlockLowlight inherits all options from CodeBlock and adds its own:

OptionTypeDefaultDescription
lowlightLowlightnull (required)The lowlight instance created with createLowlight(). Throws if not provided.
defaultLanguagestring | nullnullDefault language when none is specified on the code block
autoDetectbooleantrueAuto-detect language using lowlight.highlightAuto() when no language is set
tabIndentationbooleantrueTab key inserts spaces inside code blocks
tabSizenumber2Number of spaces per tab
OptionTypeDefaultDescription
languageClassPrefixstring'language-'CSS class prefix for the language attribute
exitOnTripleEnterbooleantrueExit code block on three consecutive Enter presses
HTMLAttributesRecord<string, unknown>{}HTML attributes for the rendered element
import { createLowlight, common, all } from 'lowlight';
// Option 1: Common languages (~40 languages)
const lowlight = createLowlight(common);
// Option 2: All languages (~190 languages)
const lowlight = createLowlight(all);
// Option 3: Specific languages only
import { createLowlight } from 'lowlight';
import javascript from 'highlight.js/lib/languages/javascript';
import typescript from 'highlight.js/lib/languages/typescript';
import css from 'highlight.js/lib/languages/css';
const lowlight = createLowlight();
lowlight.register('javascript', javascript);
lowlight.register('typescript', typescript);
lowlight.register('css', css);

CodeBlockLowlight inherits all commands from CodeBlock:

Converts the current block to a code block with an optional language.

editor.commands.setCodeBlock({ language: 'typescript' });

Toggles between a code block and a paragraph.

editor.commands.toggleCodeBlock();
editor.commands.toggleCodeBlock({ language: 'javascript' });

Access storage via editor.storage.codeBlockLowlight:

MethodReturnsDescription
listLanguages()string[]List of registered language names from the lowlight instance
const languages = editor.storage.codeBlockLowlight.listLanguages();
// ['javascript', 'typescript', 'css', 'python', ...]

CodeBlockLowlight inherits CodeBlock shortcuts and adds tab indentation:

KeyCommandDescription
Mod-Alt-CtoggleCodeBlockToggle code block (inherited)
EnterExit on triple-enterThree Enter presses exit the code block (inherited, requires exitOnTripleEnter: true)
ArrowDownExit at endArrow down at the last line exits if no node follows (inherited)
TabInsert spacesInserts tabSize spaces at cursor (only when tabIndentation: true)
Shift-TabRemove indentRemoves up to tabSize leading spaces from the current line

When tabIndentation: true (default), Tab inserts spaces (not a tab character) and Shift-Tab removes leading spaces. The number of spaces is controlled by tabSize (default: 2).

Shift-Tab only removes spaces from the beginning of the current line. It counts leading spaces and removes up to tabSize of them. If the line has no leading spaces, it does nothing.

Inherited from CodeBlock:

InputResult
```languageCreates a code block with the specified language
```Creates a code block with no language (auto-detect if enabled)

Inherited from CodeBlock:

ButtonIconGroupPriorityShortcut
codeBlockcodeBlockblocks140Mod-Alt-C

CodeBlockLowlight uses CodeBlock.extend() to inherit all CodeBlock functionality (schema, commands, input rules, toolbar items) and adds:

  1. Syntax highlighting via the lowlight plugin
  2. Tab/Shift-Tab indentation shortcuts
  3. listLanguages() storage method

The lowlightPlugin creates a ProseMirror plugin with Decoration.inline decorations for syntax tokens:

  1. Init: Highlights all code blocks in the document on first load
  2. Apply: On document changes, computes affected ranges and only re-highlights code blocks that overlap with changes (incremental)
  3. Decorations: Returns the decoration set for the current state

For each code block:

  1. Reads the language attribute (or falls back to defaultLanguage)
  2. If the language is registered, calls lowlight.highlight(language, text)
  3. If no language and autoDetect: true, calls lowlight.highlightAuto(text)
  4. Flattens the hast tree output into text tokens with CSS classes
  5. Creates Decoration.inline for each token with non-empty classes

The plugin avoids re-highlighting the entire document on every keystroke:

  1. Computes changed ranges from tr.steps mapped to final document coordinates
  2. Maps existing decorations to new positions via decorationSet.map(tr.mapping, tr.doc)
  3. Finds code blocks that overlap with changed ranges
  4. Removes stale decorations for affected blocks and re-highlights only those blocks
  5. Blocks outside the changed ranges keep their cached decorations

The lowlight option is required. If not provided, the plugin throws an error at initialization:

[@domternal/extension-code-block-lowlight] The "lowlight" option is required.
Provide a lowlight instance: CodeBlockLowlight.configure({ lowlight: createLowlight(common) })

Generates HTML with syntax-highlighted code blocks from JSON content. Unlike generateHTML() from @domternal/core, this applies lowlight highlighting to code blocks, producing <span class="hljs-keyword"> etc. inside <code> elements.

import { generateHighlightedHTML } from '@domternal/extension-code-block-lowlight';
import { createLowlight, common } from 'lowlight';
const lowlight = createLowlight(common);
const html = generateHighlightedHTML(json, extensions, lowlight, {
defaultLanguage: 'javascript',
autoDetect: false,
});
ParameterTypeDescription
contentJSONContentThe editor’s JSON content
extensionsAnyExtension[]Extensions used to generate HTML
lowlightLowlightA configured lowlight instance
optionsGenerateHighlightedHTMLOptionsOptional settings: defaultLanguage, autoDetect (default false), document

Creates a codeHighlighter callback for use with inlineStyles(). This allows server-side or email HTML rendering with syntax highlighting.

import { createLowlight, common } from 'lowlight';
import { createCodeHighlighter } from '@domternal/extension-code-block-lowlight';
import { inlineStyles } from '@domternal/core';
const lowlight = createLowlight(common);
const styled = inlineStyles(html, {
codeHighlighter: createCodeHighlighter(lowlight, {
defaultLanguage: 'javascript',
autoDetect: false,
}),
});
ParameterTypeDescription
lowlightLowlightA configured lowlight instance
optionsCreateCodeHighlighterOptionsOptional settings: defaultLanguage, autoDetect (default false)

Returns a function (code: string, language: string | null) => string | null that highlights code and returns HTML, or null if the language is not registered and autoDetect is off.

import {
CodeBlockLowlight, lowlightPluginKey,
generateHighlightedHTML, createCodeHighlighter,
} from '@domternal/extension-code-block-lowlight';
import type {
CodeBlockLowlightOptions, CodeBlockLowlightStorage, Lowlight,
GenerateHighlightedHTMLOptions, CreateCodeHighlighterOptions,
} from '@domternal/extension-code-block-lowlight';
ExportTypeDescription
CodeBlockLowlightNode (extends CodeBlock)The code block lowlight extension
lowlightPluginKeyPluginKeyThe ProseMirror plugin key for the highlight plugin
generateHighlightedHTMLFunctionGenerates HTML with syntax-highlighted code blocks from JSON content
createCodeHighlighterFunctionCreates a codeHighlighter callback for use with inlineStyles()
CodeBlockLowlightOptionsTypeScript typeOptions for CodeBlockLowlight.configure()
CodeBlockLowlightStorageTypeScript typeShape of editor.storage.codeBlockLowlight
LowlightTypeScript typeThe lowlight instance type (ReturnType<typeof createLowlight>)
GenerateHighlightedHTMLOptionsTypeScript typeOptions for generateHighlightedHTML()
CreateCodeHighlighterOptionsTypeScript typeOptions for createCodeHighlighter()

@domternal/extension-code-block-lowlight - CodeBlockLowlight.ts