highlight
PoolZero-dependency syntax highlighter - Fast, modern, tree-shakeable with React adapter and auto-scan support
@lpm.dev/neo.highlight
Modern, zero-dependency syntax highlighter for the web.
Why This?
Every existing syntax highlighter requires tradeoffs -- bloated bundles, missing TypeScript support, runtime dependencies, or no tree-shaking. @lpm.dev/neo.highlight eliminates all of them.
| Feature | neo.highlight | Prism.js | highlight.js | Shiki | react-syntax-highlighter | sugar-high |
|---|---|---|---|---|---|---|
| ESM native | Yes | No | Partial | Yes | No | Yes |
| TypeScript source | Yes | No | No | Yes | No | Yes |
| Zero dependencies | Yes | Yes | Yes | No (oniguruma) | No (7+ deps) | Yes |
| Bundle size (core) | ~3.8 KB | ~6 KB | ~30 KB | ~1 MB+ | ~40 KB+ | ~3 KB |
| Tree-shakeable | Yes | No | No | No | No | Partial |
| React adapter | Built-in | Plugin | Plugin | Plugin | Core | No |
| Auto-scan (DOM) | Built-in | Built-in | Built-in | No | No | No |
| MutationObserver | Built-in | No | No | No | No | No |
| SSR support | Yes | Manual | Manual | Yes | Yes | Yes |
| Languages | 55 | 290+ | 190+ | 190+ | 190+ | ~10 |
| Themes | 10 | 8 | 90+ | 30+ | 80+ | 1 |
| CSS custom properties | Yes | No | No | No | No | Yes |
Key Features
- Zero dependencies -- nothing to install, nothing to break
- ~3.8 KB core gzipped -- load only what you use
- 55 languages with accurate grammar definitions
- 10 themes including GitHub Dark, Dracula, Nord, Tokyo Night
- React + Vanilla JS -- first-class adapters for both
- Component mode --
<Highlight>for explicit code blocks - Auto-scan mode --
<AutoHighlight>/autoHighlight()finds and highlights<code>elements - MutationObserver -- watches for dynamically added code blocks in SPAs
- CSS custom properties -- theme values exposed as
--neo-hl-*variables - Tree-shakeable -- import only the languages and themes you need
- SSR support --
getThemeStylesheet()generates CSS at build time - Full TypeScript -- strict mode, every type exported, zero
any
Quick Start
Installation
lpm install @lpm.dev/neo.highlight
React
import { Highlight } from "@lpm.dev/neo.highlight/react";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
function App() {
return (
<Highlight language={javascript} theme={githubDark}>
{`const greeting = "Hello, world!";
console.log(greeting);`}
</Highlight>
);
}
Vanilla JS
import { highlight } from "@lpm.dev/neo.highlight/vanilla";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
const html = highlight(`const greeting = "Hello, world!";`, javascript, {
theme: githubDark,
lineNumbers: true,
});
document.getElementById("code").innerHTML = html;
React API
Import from @lpm.dev/neo.highlight/react.
<Highlight>
Renders a single code block with syntax highlighting.
import { Highlight } from "@lpm.dev/neo.highlight/react";
import { typescript } from "@lpm.dev/neo.highlight/grammars/typescript";
import { dracula } from "@lpm.dev/neo.highlight/themes/dracula";
<Highlight
language={typescript}
theme={dracula}
showLineNumbers
highlightLines={[2, 3]}
className="my-code-block"
>
{`interface User {
name: string;
email: string;
}`}
</Highlight>;
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
children |
string |
required | Source code to highlight |
language |
Grammar |
required | Grammar definition for the language |
theme |
Theme | string |
from context | Theme object or registered theme name |
showLineNumbers |
boolean |
from context | Show line numbers |
highlightLines |
number[] |
-- | Lines to highlight (1-indexed) |
classPrefix |
string |
"neo-hl" |
CSS class prefix |
className |
string |
-- | Additional CSS class for the wrapper |
style |
CSSProperties |
-- | Inline styles for the wrapper |
maxInputLength |
number |
250000 |
Maximum tokenizer input length |
maxMatchCount |
number |
100000 |
Maximum regex matches |
maxTokenCount |
number |
100000 |
Maximum token nodes |
maxRenderedLength |
number |
10000000 |
Maximum HTML output length |
maxLines |
number |
10000 |
Maximum source lines |
maxTokenDepth |
number |
100 |
Maximum grammar and token depth |
<AutoHighlight>
Scans its children for <code> elements and highlights them automatically. Uses MutationObserver to watch for dynamically added content.
Security: Sanitize untrusted HTML before you pass it to dangerouslySetInnerHTML. <AutoHighlight> does not sanitize its children.
import { AutoHighlight } from "@lpm.dev/neo.highlight/react";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { python } from "@lpm.dev/neo.highlight/grammars/python";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
<AutoHighlight languages={[javascript, python]} theme={githubDark}>
<article dangerouslySetInnerHTML={{ __html: sanitizedMarkdownHtml }} />
</AutoHighlight>;
Language is detected from class names (language-js, lang-python) or the data-language attribute on the <code> or <pre> element.
Set autoDetect to true when an element has no language hint. The detector uses token coverage and language-specific syntax.
Derived grammars need a unique marker before they can outrank their base grammar. For example, Astro needs frontmatter, and Vue needs framework directives.
This rule keeps plain HTML assigned to HTML. TypeScript, TSX, JSX, Svelte, Handlebars, SCSS, Less, and Bash use equivalent markers.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
required | Content containing <code> elements |
languages |
Grammar[] |
from context | Grammars available for detection |
theme |
Theme | string |
from context | Theme object or registered theme name |
selector |
string |
"pre code" |
CSS selector for code elements |
lineNumbers |
boolean |
from context | Show line numbers |
classPrefix |
string |
"neo-hl" |
CSS class prefix |
className |
string |
-- | Additional CSS class for the wrapper |
style |
CSSProperties |
-- | Inline styles for the wrapper |
autoDetect |
boolean |
false |
Detect blocks without a language hint |
maxInputLength |
number |
250000 |
Maximum tokenizer input length |
maxMatchCount |
number |
100000 |
Maximum regex matches |
maxTokenCount |
number |
100000 |
Maximum token nodes |
maxRenderedLength |
number |
10000000 |
Maximum HTML output length |
maxLines |
number |
10000 |
Maximum source lines |
maxTokenDepth |
number |
100 |
Maximum grammar and token depth |
onError |
(error, element) => void |
-- | Receive errors for individual blocks |
<HighlightProvider>
Provides default theme, languages, and options to all nested highlight components via React context. Avoids repeating props on every component.
import {
HighlightProvider,
Highlight,
AutoHighlight,
} from "@lpm.dev/neo.highlight/react";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { python } from "@lpm.dev/neo.highlight/grammars/python";
import { tokyoNight } from "@lpm.dev/neo.highlight/themes/tokyo-night";
function App() {
return (
<HighlightProvider
theme={tokyoNight}
languages={[javascript, python]}
lineNumbers
>
{/* All children inherit theme, languages, and lineNumbers */}
<Highlight language={javascript}>{`const x = 42;`}</Highlight>
<AutoHighlight>
<div dangerouslySetInnerHTML={{ __html: sanitizedBlogContent }} />
</AutoHighlight>
</HighlightProvider>
);
}
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
required | Child components |
theme |
Theme | string |
-- | Default theme for all children |
languages |
Grammar[] |
[] |
Default grammars for auto-highlight |
classPrefix |
string |
"neo-hl" |
Default CSS class prefix |
lineNumbers |
boolean |
false |
Default line numbers setting |
useHighlight
Hook for full control over tokenization and rendering. Returns both the raw token array and the rendered HTML string, both memoized.
import { useHighlight } from "@lpm.dev/neo.highlight/react";
import { rust } from "@lpm.dev/neo.highlight/grammars/rust";
import { nord } from "@lpm.dev/neo.highlight/themes/nord";
function CodeBlock({ code }: { code: string }) {
const { tokens, html } = useHighlight(code, rust, {
theme: nord,
lineNumbers: true,
highlightLines: [1],
wrapCode: true,
});
// Use `html` directly
return <div dangerouslySetInnerHTML={{ __html: html }} />;
// Or iterate over `tokens` for custom rendering
}
Parameters:
| Parameter | Type | Description |
|---|---|---|
code |
string |
Source code to highlight |
language |
Grammar |
Grammar definition |
options |
UseHighlightOptions |
Theme, line numbers, highlight lines, class prefix, wrapCode |
Returns: { tokens: Token[], html: string }
useHighlightContext
Access the current HighlightProvider context values directly.
import { useHighlightContext } from "@lpm.dev/neo.highlight/react";
function DebugInfo() {
const { theme, languages, classPrefix, lineNumbers } = useHighlightContext();
// ...
}
Vanilla API
Import from @lpm.dev/neo.highlight/vanilla.
highlight(code, language, options?)
Highlight source code and return an HTML string.
import { highlight } from "@lpm.dev/neo.highlight/vanilla";
import { python } from "@lpm.dev/neo.highlight/grammars/python";
import { oneDark } from "@lpm.dev/neo.highlight/themes/one-dark";
const html = highlight(
`def greet(name):
return f"Hello, {name}!"`,
python,
{
theme: oneDark,
lineNumbers: true,
highlightLines: [2],
wrapCode: true,
},
);
Options:
| Option | Type | Default | Description |
|---|---|---|---|
theme |
Theme | string |
-- | Theme object or registered name |
lineNumbers |
boolean |
false |
Show line numbers |
highlightLines |
number[] |
-- | Lines to highlight (1-indexed) |
classPrefix |
string |
"neo-hl" |
CSS class prefix |
wrapCode |
boolean |
true |
Wrap output in <pre><code> |
maxInputLength |
number |
250000 |
Maximum tokenizer input length |
maxMatchCount |
number |
100000 |
Maximum regex matches |
maxTokenCount |
number |
100000 |
Maximum token nodes |
maxRenderedLength |
number |
10000000 |
Maximum HTML output length |
maxLines |
number |
10000 |
Maximum source lines |
maxTokenDepth |
number |
100 |
Maximum grammar and token depth |
autoHighlight(options)
Scan the page for code blocks and optionally observe for dynamically added ones. Returns a cleanup function.
import { autoHighlight } from "@lpm.dev/neo.highlight/vanilla";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { typescript } from "@lpm.dev/neo.highlight/grammars/typescript";
import { css } from "@lpm.dev/neo.highlight/grammars/css";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
// Highlight all existing code blocks and watch for new ones
const cleanup = autoHighlight({
languages: [javascript, typescript, css],
theme: githubDark,
observe: true, // Use MutationObserver for SPAs
selector: "pre code", // CSS selector to match
lineNumbers: false,
});
// Later, to stop observing:
cleanup();
Language detection reads from the <code> or <pre> element:
class="language-javascript"orclass="lang-js"data-language="javascript"
scan(options)
One-shot scan. Finds all matching elements and highlights them. Returns the number of elements highlighted.
import { scan } from "@lpm.dev/neo.highlight/vanilla";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
const count = scan({
languages: [javascript],
selector: "pre code",
container: document.getElementById("article"),
force: true, // Refresh blocks that are already highlighted
onError: (error, element) => console.error(element, error),
});
console.log(`Highlighted ${count} code blocks`);
A one-shot scan applies the theme foreground and background directly to each code element. It does not add a style element.
An error in one block does not stop the scan. Use onError to receive that error.
observe(options)
Continuous scan. Highlights existing elements, then uses MutationObserver to watch for new ones. Returns a cleanup function.
import { observe } from "@lpm.dev/neo.highlight/vanilla";
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
const disconnect = observe({
languages: [javascript],
theme: githubDark,
container: document.body,
});
// Stop observing when done:
disconnect();
Web Worker
The worker entry runs tokenization outside the main thread. It contains all built-in grammars.
Create a worker module named highlight-worker.ts:
import "@lpm.dev/neo.highlight/worker";
Create the worker from the application module:
const worker = new Worker(new URL("./highlight-worker.ts", import.meta.url), {
type: "module",
});
worker.postMessage({
id: 1,
code: "const x = 42;",
language: "javascript",
maxInputLength: 100_000,
});
worker.addEventListener("message", (event) => {
if (event.data.ok) {
console.log(event.data.tokens);
} else {
console.error(event.data.error);
}
});
The worker accepts built-in language names and aliases. Each response contains the request id.
Resource limits
All entry points enforce resource limits. The limits apply to UTF-16 units, regex matches, token nodes, HTML output, lines, and nesting depth.
| Option | Default | Scope |
|---|---|---|
maxInputLength |
250000 |
Tokenizer input |
maxMatchCount |
100000 |
Regex matches |
maxTokenCount |
100000 |
Created or rendered token nodes |
maxRenderedLength |
10000000 |
Generated HTML |
maxLines |
10000 |
Rendered source lines |
maxTokenDepth |
100 |
Grammar and token-tree nesting |
You can set each limit to Infinity for trusted source code. Finite limits are safer for user-controlled source code.
CAUTION: Treat custom grammars as trusted executable regex configuration. One unsafe regex can block the JavaScript thread before a counter can stop it.
Do not accept custom grammar objects from untrusted users. For custom grammars, use a worker and terminate it after an application deadline.
The package tests every built-in grammar with adversarial input. Each regex test runs in an isolated subprocess with a hard timeout.
Core Utilities
Import from @lpm.dev/neo.highlight.
resolveGrammar(language, grammars)
Resolve a language string to a Grammar object by checking grammar names and aliases. Useful when you have a language identifier from user input (e.g. "js", "py", "tsx") and need to find the matching grammar.
import { resolveGrammar } from "@lpm.dev/neo.highlight";
import { javascript, python, typescript } from "@lpm.dev/neo.highlight/grammars";
const grammars = [javascript, python, typescript];
resolveGrammar("js", grammars); // → javascript grammar
resolveGrammar("python", grammars); // → python grammar
resolveGrammar("typescript", grammars); // → typescript grammar
resolveGrammar("unknown", grammars); // → undefined
Parameters:
| Parameter | Type | Description |
|---|---|---|
language |
string |
Language name or alias |
grammars |
Grammar[] |
Array of grammars to search in |
The lookup ignores case and surrounding whitespace in names and aliases.
Returns: Grammar | null
validateThemeContrast(theme)
Validate a theme's token colors against WCAG AA contrast requirements (4.5:1 minimum ratio). Returns a detailed per-token report.
import { validateThemeContrast } from "@lpm.dev/neo.highlight";
import { nord } from "@lpm.dev/neo.highlight/themes/nord";
const report = validateThemeContrast(nord);
// report.passed — true if all results pass
// report.theme — theme name
// report.results — array of
// { token, color, background, ratio, required, pass }
Returns: ThemeContrastReport
contrastRatio(color1, color2)
Calculate the WCAG 2.0 contrast ratio between two hex colors. Returns a value between 1 and 21.
import { contrastRatio } from "@lpm.dev/neo.highlight";
contrastRatio("#ffffff", "#000000"); // → 21
contrastRatio("#767676", "#ffffff"); // → ~4.54
meetsWCAG_AA(foreground, background, isLargeText?)
Check whether a foreground/background color pair meets WCAG AA. Normal text requires 4.5:1; large text (18px+ bold or 24px+) requires 3:1.
import { meetsWCAG_AA } from "@lpm.dev/neo.highlight";
meetsWCAG_AA("#767676", "#ffffff"); // → true (4.54:1 >= 4.5)
meetsWCAG_AA("#808080", "#ffffff"); // → false (3.95:1 < 4.5)
meetsWCAG_AA("#808080", "#ffffff", true); // → true (3.95:1 >= 3.0 for large text)
hexToRGB(hex)
Parse a hex color string ("#rgb" or "#rrggbb") to an [r, g, b] tuple.
import { hexToRGB } from "@lpm.dev/neo.highlight";
hexToRGB("#ff9e64"); // → [255, 158, 100]
hexToRGB("#fff"); // → [255, 255, 255]
relativeLuminance(r, g, b)
Calculate the WCAG 2.0 relative luminance of an RGB color. Returns a value between 0 (black) and 1 (white).
import { relativeLuminance } from "@lpm.dev/neo.highlight";
relativeLuminance(255, 255, 255); // → 1
relativeLuminance(0, 0, 0); // → 0
getDualThemeStylesheet(lightTheme, darkTheme, options?)
Generate a CSS stylesheet containing both light and dark theme variables. By default uses @media (prefers-color-scheme: dark) to switch; pass darkSelector for class-based toggling.
import { getDualThemeStylesheet } from "@lpm.dev/neo.highlight";
import { solarizedLight } from "@lpm.dev/neo.highlight/themes/solarized-light";
import { solarizedDark } from "@lpm.dev/neo.highlight/themes/solarized-dark";
// Media query (default)
const css = getDualThemeStylesheet(solarizedLight, solarizedDark);
// Class-based
const css2 = getDualThemeStylesheet(solarizedLight, solarizedDark, {
darkSelector: "[data-theme='dark']",
});
Options:
| Option | Type | Default | Description |
|---|---|---|---|
darkSelector |
string |
@media (prefers-color-scheme: dark) rule |
CSS selector to scope the dark overrides |
Languages
55 languages with tree-shakeable imports. Import only what you need.
// Import a single language
import { javascript } from "@lpm.dev/neo.highlight/grammars/javascript";
// Or import several
import {
javascript,
typescript,
python,
} from "@lpm.dev/neo.highlight/grammars";
| Language | Import | Aliases |
|---|---|---|
| JavaScript | javascript |
js, mjs, cjs |
| TypeScript | typescript |
ts, mts, cts |
| Python | python |
py |
| JSX | jsx |
react |
| TSX | tsx |
-- |
| HTML | html |
htm, xml, svg, mathml |
| CSS | css |
-- |
| SCSS | scss |
sass |
| JSON | json |
jsonc, json5 |
| YAML | yaml |
yml |
| Markdown | markdown |
md, mdx |
| GraphQL | graphql |
gql |
| Ruby | ruby |
rb |
| Go | go |
golang |
| Rust | rust |
rs |
| Java | java |
-- |
| Kotlin | kotlin |
kt, kts |
| Swift | swift |
-- |
| PHP | php |
-- |
| C | c |
h |
| C++ | cpp |
c++, cxx, cc, hpp, hxx, hh |
| C# | csharp |
cs, c#, dotnet |
| Bash | bash |
zsh |
| Shell | shell |
sh, posix-shell |
| Dockerfile | docker |
dockerfile |
| SQL | sql |
mysql, pgsql, postgres, postgresql, sqlite, plsql |
| TOML | toml |
-- |
| INI | ini |
conf, cfg, env, properties |
| Diff | diff |
patch |
| Regex | regex |
regexp |
| Lua | lua |
-- |
| Dart | dart |
-- |
| Elixir | elixir |
ex, exs |
| Scala | scala |
sc |
| R | r |
rlang |
| Svelte | svelte |
-- |
| Vue | vue |
vue-html |
| Astro | astro |
-- |
| Zig | zig |
-- |
| WASM | wasm |
wat, wast |
| Haskell | haskell |
hs |
| Erlang | erlang |
erl |
| Clojure | clojure |
clj, cljs, cljc, edn |
| OCaml | ocaml |
ml |
| Perl | perl |
pl |
| Objective-C | objectivec |
objc, obj-c |
| PowerShell | powershell |
ps1, posh |
| Terraform | terraform |
hcl, tf |
| Prisma | prisma |
-- |
| Nix | nix |
nixos |
| LaTeX | latex |
tex |
| Less | less |
-- |
| Handlebars | handlebars |
hbs, mustache |
| Solidity | solidity |
sol |
| CSV | csv |
tsv |
Themes
10 built-in themes with tree-shakeable imports.
// Import a single theme
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
// Or import several
import { githubDark, dracula, nord } from "@lpm.dev/neo.highlight/themes";
| Theme | Import | Description |
|---|---|---|
| GitHub Dark | githubDark |
GitHub's dark mode color scheme |
| GitHub Light | githubLight |
GitHub's light mode color scheme |
| One Dark | oneDark |
Atom One Dark theme |
| Dracula | dracula |
Dracula color scheme |
| Nord | nord |
Arctic, north-bluish color palette |
| Monokai | monokai |
Classic Monokai color scheme |
| Solarized Light | solarizedLight |
Ethan Schoonover's Solarized (light) |
| Solarized Dark | solarizedDark |
Ethan Schoonover's Solarized (dark) |
| Night Owl | nightOwl |
Sarah Drasner's Night Owl theme |
| Tokyo Night | tokyoNight |
Inspired by Tokyo city lights at night |
All built-in themes pass WCAG AA contrast requirements (4.5:1 minimum ratio against their background).
Theme Accessibility
Validate any theme (built-in or custom) against WCAG AA contrast requirements using validateThemeContrast():
import { validateThemeContrast } from "@lpm.dev/neo.highlight";
import { dracula } from "@lpm.dev/neo.highlight/themes/dracula";
const report = validateThemeContrast(dracula);
console.log(report.passed); // true — all colors meet the ratio
console.log(report.theme); // "dracula"
for (const result of report.results) {
console.log(
`${result.token}: ${result.ratio.toFixed(1)}:1 — ${result.pass ? "PASS" : "FAIL"}`,
);
}
Each result includes a token name, color, background, ratio, required ratio, and pass status. The foreground result checks the theme's base text color.
Dual Theme (Light/Dark)
Generate a stylesheet that switches between a light and a dark theme automatically using getDualThemeStylesheet():
Using prefers-color-scheme (default)
import { getDualThemeStylesheet } from "@lpm.dev/neo.highlight";
import { githubLight } from "@lpm.dev/neo.highlight/themes/github-light";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
const css = getDualThemeStylesheet(githubLight, githubDark);
// Produces CSS with:
// Root styles use githubLight values
// @media (prefers-color-scheme: dark) { ... } overrides with githubDark values
Using a class selector
const css = getDualThemeStylesheet(githubLight, githubDark, {
darkSelector: ".dark",
});
// Produces CSS with:
// Root styles use githubLight values
// .dark { ... } overrides with githubDark values
Inject the returned CSS into a <style> tag. Theme switching works without any JavaScript -- it follows the user's OS preference or your class toggle.
Custom Themes
Create a custom theme by defining a Theme object:
import type { Theme } from "@lpm.dev/neo.highlight";
const myTheme: Theme = {
name: "my-theme",
background: "#1a1b26",
foreground: "#c0caf5",
selection: "#33467c",
lineNumber: "#3b4261",
lineNumberActive: "#c0caf5",
lineHighlight: "#292e42",
tokenColors: {
comment: "#565f89",
keyword: "#9d7cd8",
string: "#9ece6a",
number: "#ff9e64",
boolean: "#ff9e64",
function: "#7aa2f7",
operator: "#89ddff",
punctuation: "#c0caf5",
variable: "#c0caf5",
"class-name": "#f7768e",
constant: "#ff9e64",
property: "#73daca",
tag: "#f7768e",
"attr-name": "#7aa2f7",
"attr-value": "#9ece6a",
selector: "#9ece6a",
regex: "#b4f9f8",
builtin: "#7aa2f7",
important: "#f7768e",
inserted: "#9ece6a",
deleted: "#f7768e",
changed: "#ff9e64",
namespace: "#9d7cd8",
parameter: "#e0af68",
interpolation: "#73daca",
"template-string": "#9ece6a",
decorator: "#9d7cd8",
},
};
Theme colors are applied as inline CSS custom properties (--neo-hl-*) and can be overridden in your own stylesheets:
.neo-hl {
--neo-hl-bg: #1e1e2e;
--neo-hl-fg: #cdd6f4;
--neo-hl-keyword: #cba6f7;
--neo-hl-string: #a6e3a1;
}
Use with neo.markdown
neo.highlight integrates with @lpm.dev/neo.markdown via its highlight plugin. Pass tokenize, renderToHTML, and getThemeStylesheet directly:
import { createParser } from "@lpm.dev/neo.markdown";
import { highlightPlugin } from "@lpm.dev/neo.markdown/plugins/highlight";
import { tokenize, renderToHTML, getThemeStylesheet } from "@lpm.dev/neo.highlight";
import { javascript, typescript, python } from "@lpm.dev/neo.highlight/grammars";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
const parser = createParser({
plugins: [
highlightPlugin({
grammars: [javascript, typescript, python],
tokenize,
renderToHTML,
getThemeStylesheet,
theme: githubDark,
}),
],
});
const html = parser.parse(markdownString);
Code blocks in the markdown output are automatically syntax-highlighted with proper theme colors.
SSR Support
Both the React and vanilla APIs work in server-side rendering environments out of the box. The highlight() function and <Highlight> component produce static HTML strings with inline styles -- no client-side JavaScript is required to display the highlighted code.
For server rendering, use getThemeStylesheet() to generate a CSS stylesheet string that can be injected into a <style> tag:
import { getThemeStylesheet } from "@lpm.dev/neo.highlight";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
const css = getThemeStylesheet(githubDark);
// Inject into <style> tag in your HTML template
In a Next.js or similar framework:
import { getThemeStylesheet } from "@lpm.dev/neo.highlight";
import { githubDark } from "@lpm.dev/neo.highlight/themes/github-dark";
export default function RootLayout({ children }) {
return (
<html>
<head>
<style
dangerouslySetInnerHTML={{ __html: getThemeStylesheet(githubDark) }}
/>
</head>
<body>{children}</body>
</html>
);
}
Bundle Size
Every grammar and theme is a separate module. Import only what you need.
| Import | Gzipped |
|---|---|
| Core (tokenizer + renderer) | ~3.8 KB |
| Core + 1 language | ~4.2 KB |
| React adapter | ~1.2 KB |
| Vanilla adapter | ~0.8 KB |
| Single theme | ~0.3 KB |
| Single grammar (avg) | ~0.4 KB |
| All 55 grammars | ~22 KB |
| All 10 themes | ~3 KB |
The package marks only the worker entry as side-effectful. Bundlers can tree-shake all other unused exports.
TypeScript
Written in strict TypeScript. All types are exported from the main entry point:
import type {
Token,
TokenNode,
TokenPattern,
TokenDefinition,
Grammar,
GrammarTokens,
Theme,
ThemeTokenColors,
RenderOptions,
ScanOptions,
GrammarRegistry,
} from "@lpm.dev/neo.highlight";
React-specific types:
import type {
HighlightProps,
AutoHighlightProps,
HighlightProviderProps,
UseHighlightOptions,
UseHighlightResult,
} from "@lpm.dev/neo.highlight/react";
Vanilla-specific types:
import type {
HighlightOptions,
ScanOptions,
} from "@lpm.dev/neo.highlight/vanilla";
Taxes calculated at checkout based on your location.