Vite
Highlight code in static Vite HTML entry points with Lumis.
Use @lumis-sh/vite for HTML entry points that Vite processes directly. During
transformIndexHtml the plugin parses the document to locate its code blocks, runs rehype-lumis
over each one, and splices the result back into the source.
Install
npm install -D @lumis-sh/vite
npm install @lumis-sh/lumis @lumis-sh/themes @lumis-sh/wasm-html @lumis-sh/wasm-javascriptConfigure Vite
import {defineConfig} from 'vite'
import lumis from '@lumis-sh/vite'
import {htmlMultiThemes} from '@lumis-sh/lumis/formatters'
import html from '@lumis-sh/wasm-html'
import javascript from '@lumis-sh/wasm-javascript'
import githubDark from '@lumis-sh/themes/github_dark'
import githubLight from '@lumis-sh/themes/github_light'
export default defineConfig({
plugins: [
lumis({
languages: [html, javascript],
formatter: (language) =>
htmlMultiThemes({
language,
themes: {light: githubLight, dark: githubDark},
defaultTheme: 'light-dark()',
}),
}),
],
})The plugin creates one highlighter per Vite plugin instance. It initializes that highlighter at
build or dev-server startup and reuses it for every HTML transform. Put languages in languages
to load their parsers during that startup. A language injected into another loads from its own
package, so install the packages for the languages your HTML can inject too.
Mark code in HTML
Name a language to highlight a block:
<pre><code class="language-javascript">const answer = 42</code></pre>Or put data-language on <pre>:
<pre data-language="javascript"><code>const answer = 42</code></pre>Blocks without a language are left untouched. For plain text with Lumis styling,
use language-plaintext or data-language="plaintext".
To format all unlabeled blocks, set defaultLanguage to a language name:
lumis({
defaultLanguage: 'plaintext',
formatter: (language) => htmlMultiThemes({
language,
themes: {light: githubLight, dark: githubDark},
defaultTheme: 'light-dark()',
}),
})The default is false, which skips unlabeled blocks. An omitted or undefined
value uses that default.
Add nohighlight or no-highlight to either <pre> or <code> to leave that
block untouched, even when it names a language or defaultLanguage is set:
<pre><code class="language-javascript nohighlight">filled after load</code></pre>Skipped blocks keep their original source byte for byte. A block that names an unavailable language still renders as plain text with the configured formatter.
<code> has to be the first child of <pre>. The HTML parser drops a single newline directly
after <pre>, so writing the <code> on the next line still matches, but indenting it does not:
the indentation is a text node, and the block is left alone.
<pre data-language="javascript">
<code>never highlighted</code>
</pre>Because the document is parsed as HTML before highlighting, entity-encoded source is decoded before Lumis sees it:
<pre data-language="html"><code><button>Save</button></code></pre>Authored attributes on both <pre> and <code> survive highlighting. Classes are combined,
authored styles are appended to Lumis styles, and authored values win for attributes such as
id, role, aria-*, data-*, translate, and tabindex.
Everything outside a highlighted block is returned byte for byte. The plugin parses the document
to locate the blocks, then splices the highlighted HTML into the original source at their offsets
instead of reserializing the tree, the same way Vite applies its own HTML edits. So your doctype
spelling, unquoted attributes, character references, and any <%= … %> left for a templating
plugin to fill in all reach the next plugin untouched.
Framework projects
This package is for Vite's HTML entry-point pipeline. Frameworks that handle their own entry
documents may not run transformIndexHtml; use the framework's Lumis integration instead, such
as Astro, Nuxt, or
rehype-lumis.