refractor

Lightweight virtual syntax highlighter that wraps Prism to output hast syntax trees instead of HTML strings.

Library
npm
v5.0.0
869stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
41/100Fair
Development Activity0
Maintenance32
Community44
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
78/100Good
Architecture80
Code Quality88
Innovation62
Learning Curve80

Refractor wraps Prism, the widely used browser syntax highlighter, so that highlighting produces a hast (HTML AST) tree instead of a serialized HTML string. That distinction matters for any tool built on virtual DOM diffing (React, Preact) or on unified/rehype content pipelines: instead of dropping dangerouslySetInnerHTML strings into the DOM, callers get a structured tree of element and text nodes they can diff, transform, or serialize themselves.

The package ships three entry points sized for different needs: refractor (36 common languages by default), refractor/core (zero languages, register only what you use), and refractor/all (all 290+ Prism-supported grammars). Because Prism’s own grammar files rely on global state and aren’t independently importable, refractor re-packages every supported grammar as a self-contained function that can be registered against a refractor/core instance, keeping bundle size proportional to the languages actually used.

It is the syntax-highlighting engine underneath several popular downstream packages, including react-syntax-highlighter and rehype-prism-plus, which is why most consumers never call it directly but still depend on its correctness and language coverage.

What You Get

  • A refractor.highlight(value, language) API that returns a hast Root node instead of an HTML string
  • Three tiered entry points — refractor (common languages), refractor/core (bring your own), and refractor/all (everything) — so bundle size scales with actual language usage
  • Every Prism grammar re-packaged as an importable, registerable function (refractor/markdown, refractor/rust, etc.), working around Prism’s normal reliance on global state
  • Alias support (refractor.alias) and introspection (refractor.registered, refractor.listLanguages) for managing which languages and shorthand names are active
  • Full TypeScript types generated via JSDoc + tsc, with 100% type coverage enforced in CI

Common Use Cases

  • Rendering syntax-highlighted code blocks in React or Preact apps without injecting raw HTML strings
  • Powering markdown/MDX pipelines built on remark/rehype that need highlighted code as part of the AST, not as a final HTML blob
  • Serving as the highlighting engine inside higher-level packages like react-syntax-highlighter or rehype-prism-plus
  • Building custom code renderers that target non-HTML output — ANSI terminal colors, PDF, or other tree-consuming serializers
  • Selectively bundling only the languages a docs site or editor actually needs via the refractor/core + refractor/* entry points

Under The Hood

Architecture The package is organized around a single core module (lib/core.js) that implements a Refractor class wrapping a patched copy of Prism’s tokenizer (lib/prism-core.js), with lib/common.js and lib/all.js acting as pre-configured entry points that import and register subsets of the 290+ per-language files under lang/. Each language file in lang/ is a small function of shape (prism: Refractor) => void that calls prism.languages[...] to register a grammar plus any aliases, decoupling grammar registration from Prism’s normal reliance on browser globals. Highlighting itself walks Prism’s token stream and converts it into hast element/text nodes rather than HTML markup, which is the one architectural change that differentiates this package from vanilla Prism; the script/languages.js and script/list.js build scripts regenerate the lang/ directory and its documentation table from upstream Prism on demand.

Tech Stack The project is plain ESM JavaScript ("type": "module") with no runtime framework dependency beyond hastscript (hast node construction), parse-entities (HTML entity decoding), and @types/hast/@types/prismjs for typing — prismjs itself is a dev dependency only, vendored and patched via patch-package at build time rather than depended on directly. Type safety comes from TypeScript’s checkJs mode operating directly over JSDoc-annotated .js files (tsc --build) rather than authoring .ts sources, with type-coverage enforcing 100% coverage in CI. Linting and formatting run through xo and prettier, and the whole repo is unified/remark-linted via remark-preset-wooorm, reflecting its place in Titus Wormer’s broader unified/rehype/remark ecosystem.

Code Quality Tests live under test/index.js and run via Node’s built-in node:test runner against refractor/all, covering the public API (highlight, register, alias, registered, listLanguages) plus per-language fixture comparisons stored in test/fixtures/. Coverage is enforced at 100% via c8 --check-coverage, and the test script chains generation, build, formatting, and coverage gates together, so a change that breaks typing, linting, or coverage fails CI outright. Error handling is explicit and deliberate — invalid value/language arguments throw descriptive TypeError-style messages that are directly asserted against in tests, rather than failing silently.

What Makes It Unique Refractor’s core insight is narrow but valuable: Prism is a mature, widely trusted highlighter, but its output is an HTML string, which is awkward for any tool built on ASTs or virtual DOM diffing. Refractor solves this by re-exporting Prism’s tokenizer output as hast nodes and re-packaging all of Prism’s otherwise-global-dependent grammar files as independently importable functions — without reimplementing Prism’s actual tokenization logic. That combination of AST-native output and per-language tree-shakeable imports is why it became the de facto highlighting layer for the rehype/unified ecosystem and for React-based code renderers, despite not introducing any novel highlighting algorithm itself.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search