Lumis Docs
Formatters

Formatters

Choose between HTML Inline, HTML Linked, HTML Multi-Themes, Terminal, BBCode Scoped, or custom formatters in Lumis.

Lumis separates parsing from rendering. The parser finds tokens; the formatter decides how those tokens become output.

Built-in formatter types

OutputCLIRustElixirJavaScriptJava
HTML with inline styleshtml-inlineHtmlInlineBuilder:html_inlinehtmlInline()Formatter.HTML_INLINE
HTML with CSS classeshtml-linkedHtmlLinkedBuilder:html_linkedhtmlLinked()Formatter.HTML_LINKED
HTML with CSS variables for multiple themeshtml-multi-themesHtmlMultiThemesBuilder:html_multi_themeshtmlMultiThemes()
ANSI terminal outputterminalTerminalBuilder:terminalterminal()Formatter.TERMINAL
BBCode with scope tagsbbcode-scopedBBCodeScopedBuilder:bbcode_scopedbbcodeScoped()Formatter.BBCODE

Each built-in formatter has a dedicated page with options and more examples:

HTML Inline

Self-contained output with no external CSS.

import {highlight} from '@lumis-sh/lumis';
import {htmlInline} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
import frappe from '@lumis-sh/themes/catppuccin_frappe';
const html = await highlight(
'const x = 1',
htmlInline({language: javascript, theme: frappe, preClass: 'code-block'})
);

HTML Linked

Smaller HTML. Pair with a CSS theme file.

import '@lumis-sh/themes/css/catppuccin_frappe.css';
import {highlight} from '@lumis-sh/lumis';
import {htmlLinked} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
const html = await highlight(
'const x = 1',
htmlLinked({language: javascript, preClass: 'code-block'})
);

HTML Multi-Themes

One block, multiple themes via CSS custom properties.

import {highlight} from '@lumis-sh/lumis';
import {htmlMultiThemes} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
import latte from '@lumis-sh/themes/catppuccin_latte';
import frappe from '@lumis-sh/themes/catppuccin_frappe';
const html = await highlight(
'const x = 1',
htmlMultiThemes({
language: javascript,
themes: {light: latte, dark: frappe},
defaultTheme: 'light-dark()',
})
);

Java does not support HTML Multi-Themes in lumis4j at the moment.

Tag attributes

All three HTML formatters take pre_attrs and code_attrs, so a copy button, a tab panel or an analytics hook can carry its own id, data-* or ARIA attributes without rewriting the tags afterwards.

Three rules decide what the tag ends up with:

  • class is unioned with the classes Lumis generates, which are lumis, pre_class, the theme names on a multi-theme block, and language-* on <code>
  • style is appended after the theme's own declarations
  • everything else replaces the generated value, including translate and tabindex on <code>

A value is a string, or a boolean for the two things HTML spells without one: true writes the bare name that hidden, inert and open take, and false removes an attribute Lumis would otherwise generate, which is how tabindex comes off <code>.

Values are escaped, so an attribute can hold quotes and angle brackets. Names are checked instead, because escaping cannot make one safe: a space needs no escaping and splits one name into two attributes, so x onclick=alert(1) would be an event handler however its value was treated. A name carrying a space, a quote, =, / or > is an error rather than markup.

htmlLinked({
language: javascript,
preClass: 'code-block',
preAttrs: {id: 'example', 'data-panel': 'output', class: 'overflow-x-auto'},
codeAttrs: {id: 'example-code', tabindex: false}
})

A custom formatter reaches the same values through pre_attrs, multi_themes_pre_attrs and code_attrs, described in Custom Formatters.

Adding attributes to a formatter you were handed

The options above are set when a formatter is built. Code that receives one already built — a renderer integration highlighting many blocks, each with its own attributes — uses withAttrs in JavaScript, which returns a new formatter and leaves the original alone:

import {htmlInline, withAttrs} from '@lumis-sh/lumis/formatters'
const base = htmlInline({language: javascript, theme: dracula})
const block = withAttrs(base, {preAttrs: {id: 'example', class: 'card'}})

The attributes layer over any the formatter already carried, by the same rules, and a false still removes one Lumis generates. A formatter that is not a built-in HTML one comes back untouched, since a custom render has no attribute contract to honour.

Rust and Elixir have no counterpart because they do not need one: a Rust builder takes pre_attrs before build(), and an Elixir formatter is a keyword list the caller can add to. Only a JavaScript formatter is already closed over its own options by the time you hold it.

rehype-lumis and markdown-it-lumis are both built on this, which is how a <pre> carrying an id, a data-panel and Tailwind classes survives being highlighted.

Terminal

ANSI output for the terminal.

import {highlight} from '@lumis-sh/lumis';
import {terminal} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
import frappe from '@lumis-sh/themes/catppuccin_frappe';
const ansi = await highlight(
'const x = 1',
terminal({language: javascript, theme: frappe})
);

BBCode Scoped

Scope-based BBCode output for BBCode-aware renderers and custom pipelines.

import {highlight} from '@lumis-sh/lumis';
import {bbcodeScoped} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/lumis/langs/javascript';
const output = await highlight(
'const x = "[url=x]"',
bbcodeScoped({language: javascript})
);

Custom formatters

Rust and JavaScript expose lower-level formatter APIs so you can render tokens however you want.

  • Rust: implement the Formatter trait
  • JavaScript: pass a formatter object with language and format(source), then call the sync free functions highlightIter() or highlightEvents() inside format() — see source

For deeper formatter-specific options, use the dedicated pages above. For theme data and formatter helpers, continue with Themes.

On this page