Lumis Docs
Integrations

markdown-it

Use Lumis directly in markdown-it with markdown-it-lumis.

Full example on GitHub

Use @lumis-sh/markdown-it-lumis when your renderer is built on markdown-it.

Install

npm install markdown-it @lumis-sh/markdown-it-lumis @lumis-sh/themes

Example

import MarkdownIt from 'markdown-it'
import markdownItLumis from '@lumis-sh/markdown-it-lumis'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import javascript from '@lumis-sh/lumis/langs/javascript'
import rust from '@lumis-sh/lumis/langs/rust'
import plaintext from '@lumis-sh/lumis/langs/plaintext'
import latte from '@lumis-sh/themes/catppuccin_latte'
const md = new MarkdownIt()
const lumis = await markdownItLumis({
formatter: (language) => htmlInline({language, theme: latte}),
languages: [javascript, rust, plaintext],
})
lumis(md)
const html = md.render(markdown)

Use this for tools like VitePress or for custom markdown-it renderers.

Exports

default

const lumis = await markdownItLumis(options)
lumis(md)

Creates a Lumis highlighter and returns an installer function for a markdown-it instance.

fromHighlighter

import {createHighlighter} from '@lumis-sh/lumis'
import {fromHighlighter} from '@lumis-sh/markdown-it-lumis'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import {bundledLanguages} from '@lumis-sh/lumis/bundles/web'
const highlighter = await createHighlighter({languages: [bundledLanguages]})
await highlighter.loadLanguage(bundledLanguages.javascript)
const lumis = fromHighlighter(highlighter, options)
lumis(md)

Use this when you want to reuse an existing highlighter.

Options

  • formatter - required callback that receives the fence language and returns a Lumis formatter
  • languages - languages to register or load during setup before calling sync md.render(); accepts Language objects, bundles, and string refs when a bundle is also present
  • fenceAttrsOnPre - put a fence's attributes on <pre> rather than <code>. Defaults to true

Behavior

  • overrides fenced code rendering
  • keeps the attributes a fence carries, which most highlighter plugins drop
  • loads any language refs passed in languages during async setup
  • renders synchronously after setup, so load the languages you expect to use
  • falls back to the default fence renderer if highlighting fails and no fallback succeeds

Fence attributes

markdown-it-attrs and @mdit/plugin-attrs let a fence carry attributes:

```js {#example .code-card data-panel="install"}
const x = 1
```

Those survive here, on the <pre>:

<pre class="lumis code-card" style="color: #f8f8f2; background-color: #282a36;" id="example" data-panel="install"><code class="language-javascript" translate="no" tabindex="0">

class unions with the classes Lumis generates, style is appended after the theme's declarations, and everything else replaces what Lumis would have written, translate and tabindex included. It is the same merge the formatters' own preAttrs and codeAttrs go through, because that is what the plugin hands them to.

<pre> is the placement markdown-it-attrs 5 documents, under this same fenceAttrsOnPre name. markdown-it's own fence renderer puts them on <code> instead; pass fenceAttrsOnPre: false for that.

markdownItLumis({
formatter: (language) => htmlInline({language, theme: latte}),
fenceAttrsOnPre: false,
})

Two things about markdown-it-attrs itself, neither of them Lumis':

  • Its <pre> placement only takes effect when it recognises markdown-it's default fence rule by reference, which it looks up through its own require('markdown-it'). Load markdown-it as ESM and that is a different module instance, so the check reads "custom renderer", the wrapper stands down, and the attributes land on <code>. Lumis installs a fence renderer of its own, so the wrapper stands down for it either way and the placement above is Lumis' rather than inherited.
  • It reads {...} after the language, which is also where several highlighters read their own metadata. Give it a leftDelimiter/rightDelimiter of its own if you hit that.

Most highlighter plugins drop these attributes rather than place them. @shikijs/markdown-it loses them outright, because it hooks options.highlight and returns a full <pre> block, and markdown-it's default fence renderer returns early on exactly that, never reaching the branch that renders a token's attributes.

On this page