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 / TypeScript | 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/wasm-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/wasm-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/wasm-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.
Override the block background
Use the style entry in pre_attrs (preAttrs in JavaScript) to set the
background of the wrapping <pre>. Lumis appends it after the theme's styles,
so your background-color wins while the theme's foreground color stays intact.
import {htmlInline} from '@lumis-sh/lumis/formatters'
import dracula from '@lumis-sh/themes/dracula'
htmlInline({
theme: dracula,
preAttrs: {style: 'background-color: #181818;'},
})The opening tag contains both declarations; the later one takes effect:
<pre class="lumis" style="color: #f8f8f2; background-color: #282a36; background-color: #181818;">Replace #181818 with transparent to let an enclosing card's background show
through. To use a color defined by your page's CSS, use
background-color: var(--code-block-background); and define
--code-block-background on the block or an ancestor.
The same attribute option works with html_multi_themes. It changes only the
block background; token and highlighted-line backgrounds remain unchanged.
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 the npm package, 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 formatter from the npm package 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.
Inline code
The HTML formatters write a code block by default. With structure set to
inline, they write the token spans and nothing else, for code that goes inside
markup the page already has: a <code> in a sentence, a table cell, a button.
import {highlight} from '@lumis-sh/lumis';
import {htmlInline} from '@lumis-sh/lumis/formatters';
import bash from '@lumis-sh/wasm-bash';
import frappe from '@lumis-sh/themes/catppuccin_frappe';
const spans = await highlight(
'mix igniter.install mdex',
htmlInline({language: bash, theme: frappe, structure: 'inline'})
);A block of the same source is:
<pre class="lumis" style="color: #c6d0f5; background-color: #303446;"><code class="language-bash" translate="no" tabindex="0"><span class="l-line" data-line="1"><span style="color: #8caaee;">mix</span> <span style="color: #a6d189;">igniter.install</span> <span style="color: #a6d189;">mdex</span></span></code></pre>The inline output is what the line held:
<span style="color: #8caaee;">mix</span> <span style="color: #a6d189;">igniter.install</span> <span style="color: #a6d189;">mdex</span>It goes inside an element of yours, where a <pre> or a line element would not
be valid HTML:
<p>Install it with <code><span style="color: #8caaee;">mix</span> …</code>, then restart the server.</p>Source with several lines keeps its newlines. Lines are separated by \n, the
last one has no terminator, and a token that crosses a line is closed before the
newline and reopened after it, as in a block. What belongs to the block is left
out:
- the
<pre>, the<code>and the<span class="l-line">elements - the theme's text and background color, which a block writes on its
<pre>. Text outside a token takes the color of the element around it - whatever
pre_class,pre_attrs,code_attrs,highlight_lines,line_numbersandheaderwould add. These options are ignored rather than rejected, so code that highlights both code blocks and inline code can pass the same options to both and addstructurefor the inline ones - the
data-lumis-budgetmarker, which is an attribute of the<pre>
Keeping the theme's colors
To give inline code the theme's text and background color, put them on your own
element. pre_attrs and multi_themes_pre_attrs return the class and style a
block's <pre> would get, and open_tag writes them on any tag:
import {openTag, preAttrs} from '@lumis-sh/lumis/formatters/html';
const html = `${openTag('code', preAttrs({theme: frappe}))}${spans}</code>`;
// <code class="lumis" style="color: #c6d0f5; background-color: #303446;">…</code>In JavaScript, toInline from @lumis-sh/lumis/formatters derives both from
a formatter you already have: the same formatter with structure: 'inline', and
the attributes for your element, merged with any you pass:
import {toInline} from '@lumis-sh/lumis/formatters';
const inline = toInline(htmlInline({language: bash, theme: frappe}), {id: 'install'});
const html = `${openTag('code', inline.attrs)}${await highlight(source, inline.formatter)}</code>`;The Markdown and HTML integrations use it for their inlineCode option; see
rehype-lumis.
With HTML Linked, add the lumis class to your element and the theme stylesheet
colors it. The stylesheet's line layout rules only apply inside pre.lumis, so
they leave inline code alone.
light-dark() colors from HTML Multi-Themes follow the color-scheme of the
element they are in, as they do in a block. Leave it unset to inherit the page's
choice, or set color-scheme: light dark on your element to follow the OS
preference. Inline output ignores pre_attrs and code_attrs; put the scheme
on your own element. See Light/Dark Mode.
Terminal
ANSI output for the terminal.
import {highlight} from '@lumis-sh/lumis';
import {terminal} from '@lumis-sh/lumis/formatters';
import javascript from '@lumis-sh/wasm-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/wasm-javascript';
const output = await highlight(
'const x = "[url=x]"',
bbcodeScoped({language: javascript})
);Custom formatters
Rust and the npm package expose lower-level formatter APIs so you can render tokens however you want.
- Rust: implement the
Formattertrait - JavaScript / TypeScript: pass a formatter object with
render(source, events)and an optionallanguage. Lumis supplies the highlighted events; use the synchighlightIter()orhighlightEvents()helpers insiderender()only if you need to highlight more source — see the Formatter interface
For deeper formatter-specific options, use the dedicated pages above. For theme data and formatter helpers, continue with Themes.