TanStack Markdown does not bundle a tokenizer, language definitions, themes, CSS, or an asynchronous grammar runtime. It accepts one synchronous CodeHighlighter callback.
import { renderHtml } from '@tanstack/markdown/html'
import {
renderNodesToHtml,
renderTokens,
tokenize,
} from '@tanstack/highlight'
const html = renderHtml(source, {
highlighter(code, lang, options) {
const result = tokenize(code, { lang })
return renderNodesToHtml(
renderTokens(result.tokens, {
lineNumbers: options?.lineNumbers,
decorations: options?.highlightLines?.map((line) => ({
lines: line,
className: 'is-highlighted',
})),
}),
)
},
})The exact adapter depends on the highlighter’s output contract. The callback receives:
```tsx file="app.tsx" framework="react" {2,4-6}
const one = 1
const two = 2
```The parser records metadata even when no highlighter is configured. This keeps content parsing independent from presentation and lets a build pipeline change highlighters without rebuilding the AST.
The callback must return markup for the contents of <code>, not a surrounding <pre> or <code> element. The renderer supplies those containers. For example, do not pass TanStack Highlight's highlightToHtml directly because that convenience function returns its own complete <pre><code> tree.
Highlighter output is inserted as trusted HTML in both renderers. The highlighter must escape source text and return markup that is safe for the content being rendered.
For static blogs and docs, parse and highlight during a build or server render, then send the finished markup. The Markdown package does not force highlighting into client bundles.