
rangi is a lightweight and dependency-free JavaScript syntax highlighter that converts source code into self-contained HTML, ANSI terminal text, or raw tokens.
Every highlighted result is fully self-contained. By default, theme colors are inlined as style attributes. The code block needs no external stylesheet, client-side JavaScript, or hydration.
You can also switch to class-based rendering, use a CSS custom properties theme, or work with raw tokens for custom markup.
The library supports 46+ programming languages, 25 themes (including ready-made light/dark modes), automatic language detection, and a tree-shakable core for small bundles.
Features:
- Self-contained HTML with inline theme colors.
- 46 bundled language grammars plus common aliases.
- Automatic language detection with a plain-text fallback.
- Default light/dark colors based on the reader’s color scheme.
- Selective grammar and theme imports through
rangi/core. - Class-only markup for projects with an existing stylesheet.
- CSS custom properties for runtime palette changes.
- Raw token output for JSX, custom markup, and other renderers.
- ANSI color output for terminal commands and developer tools.
- Synchronous rendering with zero runtime dependencies.
How To Use It
Installation
Install rangi through NPM for a bundler, Node.js project, static-site generator, or web app with an ESM build step.
npm install rangi
Basic Usage
codeToHtml() accepts a source string and a language key. It returns a complete <div> or <code> string with escaped source text and inline theme colors. Add a target container, then insert the generated markup.
<div id="code-preview"></div>
import { codeToHtml } from "rangi";
const source = `const discount = total * 0.15;
console.log(discount);`;
const highlightedHtml = codeToHtml(source, {
lang: "js",
});
document.querySelector("#code-preview").innerHTML = highlightedHtml;
Use a Light/Dark Theme Pair
Pass one theme object for a fixed palette or a { light, dark } pair for automatic color-scheme changes. rangi writes the pair with the CSS light-dark() function inside the generated styles.
import { codeToHtml } from "rangi";
import { githubDark, githubLight } from "rangi/themes";
const highlightedHtml = codeToHtml(source, {
lang: "ts",
theme: {
light: githubLight,
dark: githubDark,
},
});
Detect the Language
detectLanguage() scores the complete input against the bundled grammars. Full files produce stronger results than isolated middle fragments, especially for formats such as JSON that rely on document structure.
import { codeToHtml, detectLanguage } from "rangi";
const language = detectLanguage(source);
const highlightedHtml = codeToHtml(source, {
lang: language,
});
Build a Smaller Bundle with rangi/core
The main entry includes every grammar and both default themes. The core entry includes no language or theme data. Import every delegated grammar required by the selected language. JavaScript highlighting uses separate grammars for JSDoc, template literals, regular expressions, and TODO markers.
import { codeToHtml } from "rangi/core";
import {
js,
js_template_literals,
jsdoc,
regex,
todo,
} from "rangi/languages";
import { githubDark } from "rangi/themes";
const highlightedHtml = codeToHtml(source, {
lang: "js",
languages: {
js,
js_template_literals,
jsdoc,
regex,
todo,
},
theme: githubDark,
});
Emit CSS Classes
Set classes: true when a shared stylesheet controls layout and token colors. This mode removes every inline style attribute. The stylesheet must define the block layout, including white-space: pre, as well as the token palette.
import { codeToHtml } from "rangi/core";
import { css } from "rangi/languages";
const highlightedHtml = codeToHtml(source, {
lang: "css",
languages: { css },
classes: true,
});
.shj {
box-sizing: border-box;
max-width: 100%;
overflow: auto;
padding: 1rem;
white-space: pre;
font: 0.95rem/1.6 Consolas, "Courier New", monospace;
background: #0d1117;
color: #e6edf3;
}
.shj-kwd,
.shj-oper {
color: #ff7b72;
}
.shj-str {
color: #a5d6ff;
}
.shj-cmnt {
color: #8b949e;
font-style: italic;
}
Render Inline Code
Set inline: true for a <code> element that fits inside prose. Block output uses a <div> and chooses a one-line or multi-line layout from the presence of a line break.
const inlineCode = codeToHtml("Array.from()", {
lang: "js",
inline: true,
});
Get Raw Tokens
tokenize() returns tokens in source order. Each item contains text and, when matched, a token type. Public token types are deleted, err, var, section, kwd, class, cmnt, insert, type, func, bool, num, oper, str, esc, and bracket. The text stays unescaped, and joining every text value recreates the original source.
import { tokenize } from "rangi";
const tokens = tokenize("let count = 3", {
lang: "js",
});
// [
// { text: "let", type: "kwd" },
// { text: " count " },
// { text: "=", type: "oper" },
// { text: " " },
// { text: "3", type: "num" }
// ]
Highlight Terminal Output
codeToAnsi() returns a string with 24-bit ANSI color sequences. printHighlight() writes the result to the terminal, and the package also exposes a rangi command for highlighting a file.
import { codeToAnsi, printHighlight } from "rangi";
import { atomDark } from "rangi/themes";
const terminalText = codeToAnsi(source, {
lang: "js",
theme: atomDark,
});
console.log(terminalText);
printHighlight(source, {
lang: "js",
theme: atomDark,
});
npx rangi src/index.ts
Rendering Options
lang(string, default:"plain"): Selects a bundled grammar, alias, or custom grammar key.languages(object): Supplies custom or selectively imported grammars. The main entry already includes all bundled grammars.rangi/corerequires this option.theme(object or light/dark pair): Sets token, foreground, background, and line-number colors.rangi/corerequires a theme unlessclasses: trueis active.inline(boolean, default:false): Returns an inline<code>element instead of a block<div>.lineNumbers(boolean, default:true): Controls the gutter on multi-line output.classes(boolean, default:false): Emitsshj-*class names and removes inline style attributes.
Supported Languages
The bundled language keys are asm, astro, bash, c, cpp, cs, css, csv, dart, diff, docker, go, graphql, html, http, ini, java, js, jsdoc, json, jsx, kt, less, log, lua, make, md, php, pl, plain, ps1, py, rb, regex, rs, scss, sql, svelte, swift, toml, ts, tsx, uri, vue, xml, and yaml.
Aliases include javascript, mjs, and cjs for JavaScript; typescript, mts, and cts for TypeScript; jsonc, json5, jsonl, and ndjson for JSON; python for Python; shell and zsh for Bash; svg for XML; and yml for YAML. Aliases work as lang values and as named exports from rangi/languages.
API Methods
// Return a complete highlighted <div> or <code> string.
codeToHtml(code, options);
// Return the highlighted content inside the outer block.
highlightText(code, options);
// Return ordered raw tokens for a custom renderer.
tokenize(code, { lang, languages });
// Return the best matching bundled language key or "plain".
detectLanguage(code);
// Return a highlighted string with 24-bit ANSI colors.
codeToAnsi(code, { lang, languages, theme });
// Write highlighted ANSI output to the terminal.
printHighlight(code, { lang, languages, theme });
Themes and CSS Customization
Individual palettes are named exports from rangi/themes. Families with matching light and dark variants also expose ready-made pairs: catppuccin, everforest, geist, github, gruvbox, solarized, and vscodeModern.
import { codeToHtml } from "rangi";
import { dracula, github, tokyoNight } from "rangi/themes";
codeToHtml(source, { lang: "js", theme: dracula });
codeToHtml(source, { lang: "js", theme: github });
codeToHtml(source, { lang: "js", theme: tokyoNight });
The cssVariables theme keeps the layout styles inline and resolves the palette through custom properties. Undefined token properties inherit --shj-fg. Define the variables on :root, a theme container, or an individual code block. These variables cover the complete palette surface in compact groups.
| CSS variables | Purpose |
|---|---|
--shj-bg, --shj-fg | Block background and default foreground colors. |
--shj-numbers | Line-number gutter color. |
--shj-kwd, --shj-oper, --shj-esc | Keywords, operators, and escape sequences. |
--shj-cmnt, --shj-bracket | Comments and brackets. |
--shj-num, --shj-bool, --shj-type | Numbers, booleans, and type names. |
--shj-str, --shj-func, --shj-class | Strings, function names, and class names. |
--shj-var, --shj-section | Variables and section markers. |
--shj-insert, --shj-deleted, --shj-err | Inserted, deleted, and error tokens. |
import { codeToHtml } from "rangi";
import { cssVariables } from "rangi/themes";
const highlightedHtml = codeToHtml(source, {
lang: "js",
theme: cssVariables,
});
-root {
--shj-bg: #0d1117;
--shj-fg: #e6edf3;
--shj-numbers: #8b949e;
--shj-kwd: #ff7b72;
--shj-oper: #ff7b72;
--shj-cmnt: #8b949e;
--shj-num: #79c0ff;
--shj-str: #a5d6ff;
--shj-func: #d2a8ff;
--shj-insert: #7ee787;
--shj-deleted: #ffa198;
--shj-err: #ffa198;
}
CSS Class Reference
| Class | Element or role |
|---|---|
shj | Base class on every highlighted block or inline element. |
shj-lang-<lang> | Escaped language key for language-specific styling. |
shj-inline | Inline <code> output. |
shj-oneline | Single-line block output. |
shj-multiline | Multi-line block output. |
shj-scroll | Scroll container around multi-line content. |
shj-numbers | Line-number gutter. |
shj-code | Highlighted code column beside the gutter. |
shj-<type> | Token class such as shj-kwd, shj-str, or shj-cmnt. |
Alternatives:
- Tiny & Fast JavaScript Syntax Highlighting Library – Speed Highlight
- Add Beautiful Code Highlighting with Syntax.js
- Syntax Highlighter For JSX – Sugar High
- Easy Syntax Highlighting Library – CSPSH.js
FAQs:
Q: Does rangi require a build step or bundler?
A: No. A single import { codeToHtml } from "rangi" runs in Node.js, the browser, or a worker without a build step.
Q: How does rangi pick a language when none is specified?
A: Call detectLanguage(code) and pass its result as the lang option. It scores the code against every bundled grammar and returns plain when nothing scores high enough.
Q: Does rangi support React, Vue, or other frameworks directly?
A: rangi has no framework-specific bindings. codeToHtml returns a plain HTML string, which fits dangerouslySetInnerHTML in React or v-html in Vue, and tokenize returns raw tokens for a component that renders its own markup.
Q: Why does the terminal output ignore the cssVariables theme?
A: A terminal has no stylesheet to resolve custom properties against. A theme value that isn’t a literal hex color leaves its tokens uncolored in the terminal, and no escape sequence is produced for it.







