A tiny, dependency-free syntax highlighter for the web.
MicroLighter uses the CSS Custom Highlight API and TextMate
grammars. It highlights code without adding a <span> around every token, so
your markup stays clean and editable.
- About 2 KiB compressed
- No runtime dependencies
- 35 languages, loaded on demand
- 10 bundled themes
- Clean DOM with no token markup
- Programmatic, automatic, and web component APIs
- Editable code block support
npm install microlighterAdd a language class to your code:
<pre><code class="language-javascript">const answer = 42;</code></pre>Import a theme and run the highlighter:
import "microlighter/themes/github.css";
import { highlightAll } from "microlighter";
document.body.dataset.syntaxTheme = "github";
await highlightAll();That's it. MicroLighter finds every pre > code block with a supported
language and highlights it.
The main package exports highlightAll(). Importing it has no side effects.
import { highlightAll } from "microlighter";
await highlightAll();Pass options to limit the scan or add project-specific language aliases:
await highlightAll({
root: document.querySelector("#docs"),
selector: "pre.code > code",
languageAliases: {
ecmascript: "javascript",
shellsession: "bash"
}
});| Option | Default | Description |
|---|---|---|
root |
document |
Element or document to search |
selector |
"pre > code" |
Selector used to find code blocks |
languageAliases |
{} |
Extra aliases mapped to bundled grammars |
highlightAll() returns a promise containing the highlighted code elements.
Import the auto runner to highlight the page as soon as the module loads:
<link rel="stylesheet" href="./node_modules/microlighter/themes/github.css">
<script type="module" src="./node_modules/microlighter/microlighter.min.js"></script>
<body data-syntax-theme="github">After adding or changing code, dispatch this event to highlight again:
document.dispatchEvent(new Event("syntax-highlight"));The event can also bubble from a code block or one of its parents.
Use the auto runner directly from a CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/microlighter@2/themes/github.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/microlighter@2/microlighter.min.js"></script>
<body data-syntax-theme="github">
<pre><code class="language-javascript">const answer = 42;</code></pre>
</body>Import the optional <micro-lighter> custom element:
<link rel="stylesheet" href="./node_modules/microlighter/themes/github.css">
<script type="module" src="./node_modules/microlighter/micro-lighter-element.min.js"></script>
<body data-syntax-theme="github">
<micro-lighter language="javascript" controls="copy" line-numbers>
<pre><code>const answer = 42;</code></pre>
</micro-lighter>
</body>| Attribute | Description |
|---|---|
language |
Language to use. Overrides language metadata on <pre> or <code> |
controls="copy" |
Adds a copy button |
line-numbers |
Adds a line number gutter without changing copied code |
Style the controls with ::part(copy-button) and ::part(line-numbers).
The recommended format is a language-* class:
<pre><code class="language-typescript">const answer: number = 42;</code></pre>You can also use data-language on <code> or <pre>:
<pre data-language="typescript"><code>const answer: number = 42;</code></pre>Avoid <pre lang="typescript"> in new code. MicroLighter supports it for
compatibility, but the HTML lang attribute should describe human language.
Common aliases work automatically:
| Aliases | Language |
|---|---|
js, jsx |
javascript |
ts |
typescript |
sh, shell, zsh |
bash |
yml |
yaml |
md |
markdown |
sass |
scss |
docker |
dockerfile |
py |
python |
rb |
ruby |
gql |
graphql |
Custom aliases passed to highlightAll() must point to a bundled language.
MicroLighter includes these grammars:
assembly, bash, c, cpp, csharp, css, dart, dockerfile, elixir,
git-diff, go, graphql, heex, html, java, javascript, json,
kotlin, lua, markdown, objective-c, perl, php, powershell,
python, r, ruby, rust, scss, sql, svelte, swift, toml, tsx,
typescript, vue, and yaml.
Grammars are ES modules and load on demand.
Load one theme and set the matching data-syntax-theme value on <body> or
any container:
<link rel="stylesheet" href="./node_modules/microlighter/themes/night-owl.css">
<section data-syntax-theme="night-owl">
<!-- code blocks -->
</section>Bundled themes:
cobalt2draculagithubminmonokainight-owlsolarized-lighttokyo-nightvespervscode-plus
Themes use CSS custom properties for a small color palette:
| Property | Token categories |
|---|---|
--syntax-comment |
Comments and quotes |
--syntax-keyword |
Keywords, storage, at-rules, and sections |
--syntax-operator |
Operators and punctuation |
--syntax-string |
Strings, regular expressions, links, and attribute values |
--syntax-constant |
Numbers, booleans, constants, symbols, and entities |
--syntax-function |
Functions, decorators, and animations |
--syntax-type |
Types and support tokens |
--syntax-variable |
Variables and interpolation |
--syntax-property |
Properties, keys, and attribute names |
--syntax-tag |
Tags |
--syntax-selector |
Selectors |
--syntax-inserted |
Inserted text |
--syntax-deleted |
Deleted text |
MicroLighter keeps code as plain text, so code blocks can remain editable. Call the highlighter after each change:
<editable-code>
<pre><code class="language-javascript">const answer = 42;</code></pre>
</editable-code>
<script type="module">
import "microlighter/microlighter.min.js";
class EditableCode extends HTMLElement {
connectedCallback() {
const code = this.querySelector("pre > code");
if (!code) return;
code.contentEditable = "plaintext-only";
code.spellcheck = false;
code.setAttribute("aria-label", "Editable code");
this.addEventListener("input", () => {
this.dispatchEvent(new Event("syntax-highlight", { bubbles: true }));
});
}
}
customElements.define("editable-code", EditableCode);
</script>Keep <pre><code> in the light DOM so MicroLighter can find it.
MicroLighter reads TextMate grammars with the browser's native RegExp. It
turns matching token ranges into Highlight objects and styles them with
::highlight(). It does not use Oniguruma, WebAssembly, or generated token
markup.
This keeps the library small. The trade-off is less language coverage and grammar accuracy than larger tools such as Shiki.
The low-level tokenizer is available from microlighter/highlight.js for
advanced integrations.
Requires Node.js 18 or newer.
npm install
npm run build
npm testUseful commands:
| Command | Description |
|---|---|
npm run build |
Build dist/, update the local demo package, and report sizes |
npm test |
Build and run Node.js and Playwright tests |
npm run size |
Print the size report without changing files |
npm run docs:update-homepage-stats |
Update the homepage bundle size |
npx serve docs |
Serve the demo at http://localhost:3000 |
The build fails when the minified bundle exceeds the gzip size limit in
package.json.
See CONTRIBUTING.md for the project structure and guides for adding grammars and themes.
MicroLighter builds on TextMate grammars and work across the syntax highlighting community:
- Prism.js inspired the semantic CSS categories.
- Shiki is the full-featured TextMate highlighter for the web.
- Bramus Van Damme's Custom Highlight API article popularized this rendering technique.
- Related projects include textmate-highlighter, syntax-highlight-element, shiki-highlight-api, and syntaxp.
MIT © Dave Rupert