LumisDocs
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

OutputCLIRustElixirJavaScript / TypeScriptJava
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/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:

  • 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.

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_numbers and header would 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 add structure for the inline ones
  • the data-lumis-budget marker, 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 Formatter trait
  • JavaScript / TypeScript: pass a formatter object with render(source, events) and an optional language. Lumis supplies the highlighted events; use the sync highlightIter() or highlightEvents() helpers inside render() 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.

On this page