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
| Output | CLI | Rust | Elixir | JavaScript | Java |
|---|---|---|---|---|---|
| HTML with inline styles | html-inline | HtmlInlineBuilder | :html_inline | htmlInline() | Formatter.HTML_INLINE |
| HTML with CSS classes | html-linked | HtmlLinkedBuilder | :html_linked | htmlLinked() | Formatter.HTML_LINKED |
| HTML with CSS variables for multiple themes | html-multi-themes | HtmlMultiThemesBuilder | :html_multi_themes | htmlMultiThemes() | — |
| ANSI terminal output | terminal | TerminalBuilder | :terminal | terminal() | Formatter.TERMINAL |
| BBCode with scope tags | bbcode-scoped | BBCodeScopedBuilder | :bbcode_scoped | bbcodeScoped() | 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:
classis unioned with the classes Lumis generates, which arelumis,pre_class, the theme names on a multi-theme block, andlanguage-*on<code>styleis appended after the theme's own declarations- everything else replaces the generated value, including
translateandtabindexon<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
Formattertrait - JavaScript: pass a formatter object with
languageandformat(source), then call the sync free functionshighlightIter()orhighlightEvents()insideformat()— see source
For deeper formatter-specific options, use the dedicated pages above. For theme data and formatter helpers, continue with Themes.