rehype-pretty-code

A rehype plugin that transforms Markdown and MDX code blocks into beautifully styled, Shiki-highlighted HTML with line highlighting, captions, and multi-theme support.

Library
npm
v0.14.5
1,317stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
62/100Good
Development Activity48
Maintenance48
Community56
Maturity56
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture80
Code Quality82
Innovation83
Learning Curve75

rehype-pretty-code is a rehype plugin that turns plain code blocks in Markdown and MDX into richly styled, syntax-highlighted HTML using Shiki — the same tokenizer that powers VS Code’s own highlighting. Rather than shipping a client-side highlighter, it does the work at build time, walking the HAST tree produced by remark/rehype, detecting fenced code blocks and inline code, and replacing them with accurately colored <pre>/<code> markup annotated with data attributes for language, theme, and line state.

Beyond baseline highlighting, it supports meta-string annotations for highlighting whole lines or specific characters/tokens, titles and captions on code blocks, multiple simultaneous themes (light/dark) via CSS variables, line numbers with configurable start offsets, and compatibility with Shiki transformers for diff-style annotations — all configurable through a single options object passed to the plugin. It’s framework-agnostic at the rehype level, which is why the project ships worked integration examples for Next.js, Astro, SvelteKit, and even plain CDN usage.

What You Get

  • Shiki-based syntax highlighting for over 100 languages resolved at build/compile time, not in the browser
  • Meta-string syntax for highlighting specific lines ({1,3-5}) or characters/tokens (/foo/) directly in fenced code block info strings
  • Support for dual/multiple themes (e.g. light and dark) applied via CSS custom properties without extra JS
  • Titles and captions attached to code blocks, plus optional line numbers with configurable starting offsets
  • Visitor hooks (onVisitLine, onVisitHighlightedLine, onVisitHighlightedChars, onVisitTitle, onVisitCaption) for injecting custom HAST nodes during processing
  • Compatibility with Shiki transformers (e.g. @shikijs/transformers) for diff notation, focus, and other decorations

Common Use Cases

  • Rendering documentation sites built with Astro, Next.js, or SvelteKit that need VS-Code-quality code blocks
  • Adding line-highlighting to tutorial content to draw attention to the specific lines being discussed
  • Building blog or docs platforms that need both a light- and dark-themed code block from a single Markdown source
  • Replacing client-side highlighters like Prism or highlight.js with a zero-runtime-JS, build-time alternative

Under The Hood

Architecture The plugin is a single unified/rehype transformer factory (rehypePrettyCode(options)) that returns an async Transformer<Root, Root> operating on the HAST tree in two passes: first via unist-util-visit to collect the set of languages that need loading into a lazily-created, module-level cached Shiki Highlighter (deduplicated per JSON-stringified theme key), then a second pass that replaces <pre><code> (block) and bare <code> (inline) nodes with Shiki-rendered fragments through an internal apply() function, which retags elements, injects data-* attributes, and assembles title/caption sibling nodes. Line- and character-level highlighting logic is factored into a dedicated chars/ subdirectory (charsHighlighter.ts, getElementsToHighlight.ts, wrapHighlightedChars.ts) that consumes meta-string-derived ranges and ids independently of the core tree traversal, keeping highlighting concerns separate from AST walking. The design is largely stateless per invocation aside from the shared highlighter cache, so swapping the underlying highlighter (via the getHighlighter option) touches configuration, not traversal logic.

Tech Stack TypeScript (98.5% of the codebase) targeting Node >=18, built with tsup into ESM output with generated .d.ts files, tested with vitest, and linted/formatted with Biome. Runtime dependencies are minimal and purposeful: shiki as a peer dependency for tokenization, unified + unist-util-visit for AST traversal, hast-util-to-string, rehype-parse for reparsing generated HTML fragments back into HAST, and parse-numeric-range for expanding range syntax like {1,3-5}. The repository is a pnpm/Turborepo monorepo containing this core package, a sibling @rehype-pretty/transformers add-on package, framework example apps (Astro, SvelteKit, Next.js, plain CDN), and a dedicated Astro-based documentation site.

Code Quality Tests live under an extensive fixture-driven suite that runs markdown fixtures through the real remark -> mdast-util-to-hast -> rehypePrettyCode -> hast-util-to-html pipeline and compares against committed expected HTML snapshots, favoring true end-to-end coverage over mocked units. Error handling leans on graceful fallback — an unrecognized language falls back to plaintext highlighting via try/catch rather than throwing. Naming is descriptive and consistently typed (isBlockCode, isInlineCode, parseBlockMetaString), with public types (Options, LineElement, CharsElement, Theme) exported for consumers. A few complexity lint rules are explicitly suppressed with inline comments rather than silently disabled, which reads as acknowledged technical debt rather than hidden risk.

What Makes It Unique The project’s defining feature is a compact meta-string mini-language for expressing line and character highlights directly inside a fenced code block’s info string, letting content authors annotate examples without writing any JavaScript. Combined with a visitor-hook system (onVisitLine and friends) that lets consumers inject custom markup during processing, this extensibility is what made it the foundation for ecosystem add-ons such as the sibling @rehype-pretty/transformers package’s copy-button and foldable-lines features. The public API defaults to sensible zero-config behavior (a built-in dark theme, background colors preserved, grid layout enabled) while still exposing power-user knobs like custom highlighters, meta-string filtering, and multi-theme output.

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