Text stays text.
No span around each token. Selection, undo and your framework keep the original text node.
A little highlight with no markup.
No span around each token. Selection, undo and your framework keep the original text node.
Prism, Shiki, Highlight.js, Lezer, or anything that returns { start, end, type }.
GitHub to Catppuccin, Shiki theme colors, or your own.
<pre><code data-lighthigh="javascript">
const answer = 42
</code></pre>
<script defer src="https://cdn.jsdelivr.net/npm/lighthigh@1"
data-theme="github-light"></script>npm i lighthigh prismjsimport highlight from 'lighthigh'
import { prism } from 'lighthigh/adapters.js'
import Prism from 'prismjs'
const code = document.querySelector('code')
highlight(code, source =>
prism(Prism.tokenize(source, Prism.languages.js))
)npm i lighthigh shikiimport highlight from 'lighthigh'
import { shiki } from 'lighthigh/adapters.js'
import { codeToTokens } from 'shiki'
const code = document.querySelector('code')
const result = await codeToTokens(code.textContent, {
lang: 'javascript', theme: 'github-light'
})
const { tokens, theme } = shiki(result)
highlight(code, tokens, { theme })npm i lighthigh highlight.jsimport highlight from 'lighthigh'
import { hljs } from 'lighthigh/adapters.js'
import hljsCore from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
hljsCore.registerLanguage('javascript', javascript)
const code = document.querySelector('code')
highlight(code, source =>
hljs(hljsCore.highlight(source, { language: 'javascript' }))
)npm i lighthigh @lezer/javascript @lezer/highlightimport highlight from 'lighthigh'
import { lezer } from 'lighthigh/adapters.js'
import { parser } from '@lezer/javascript'
import { highlightTree, classHighlighter } from '@lezer/highlight'
const code = document.querySelector('code')
highlight(code, source =>
lezer(parser.parse(source), highlightTree, classHighlighter)
)npm i lighthigh gpu-lexerimport highlight from 'lighthigh'
import { parse } from 'gpu-lexer'
const code = document.querySelector('code')
const tokens = await parse(code.textContent)
highlight(code, tokens)The renderer is 1.4 KB gzipped, no dependencies. The script with 14 languages and 18 palettes is 24 KB.
Shiki colors work as-is. For others, pass a type → color map. Colors only: no bold or italics.
Chrome and Edge 105+, Safari 17.2+, Firefox 140+. Older ones show plain text.
Call highlight(el, tokens) after render. It returns a cleanup for unmount.
Tokenize there, send [start, end, type] tuples, paint in the browser.