expressive-code
A framework-agnostic engine that renders source code as richly annotated, syntax-highlighted HTML.
Repository Health
Technical Analysis
Expressive Code is the core engine behind the Expressive Code ecosystem, bundling the plugin architecture, Shiki-based syntax highlighting, text markers, and editor/terminal frames into one convenient package. It converts plain code blocks into a HAST tree that can be rendered to HTML, giving every annotation - highlighted lines, inline text markers, diff additions and deletions, window frames with titles - a consistent, themeable output without depending on any client-side framework.
While most users reach Expressive Code through a higher-level integration such as astro-expressive-code or rehype-expressive-code, this package is the low-level building block those integrations are built on. It exposes the ExpressiveCode class (an ExpressiveCodeEngine subclass) that wires up the default plugins - Shiki, Frames, and Text Markers - and lets integration authors call render() directly, collect the generated base/theme styles and JS modules, and hand the resulting AST to their own templating layer.
Because every annotation is implemented as a plugin hook rather than a hardcoded rendering step, Expressive Code can be extended with custom annotations (collapsible sections, line numbers, and more ship as separate official plugins) without forking the core renderer. The engine also handles theme-aware CSS generation, including automatic light/dark mode switching and WCAG contrast adjustments for syntax highlighting colors.
What You Get
- The
ExpressiveCodeclass, a preconfiguredExpressiveCodeEnginethat bundles the Shiki, Frames, and Text Markers plugins by default - Shiki-powered syntax highlighting using the same TextMate grammars and themes as VS Code
- Text marker annotations for highlighting full lines or inline ranges, including inserted/deleted diff styling
- Editor and terminal window frames with optional tab or window-caption titles
- Automatic generation of base styles, per-theme CSS variables, and any required client-side JS modules
- A plugin hook system (
ExpressiveCodePlugin) for adding fully custom annotations without touching the core renderer
Common Use Cases
- Building a markdown/MDX code-block integration - authors of remark/rehype plugins or static site generators use
expressive-codedirectly to render fenced code blocks to HTML - Custom documentation tooling - teams building an in-house docs pipeline call
render()on code snippets and inject the returned styles and HTML into their own page templates - Framework integration authoring - anyone building an Expressive Code integration for a new framework starts from this package instead of reimplementing highlighting and annotation logic
- Programmatic code-to-HTML generation - scripts and tools that need to convert source snippets into styled, shareable HTML (e.g. for blog posts, changelogs, or code-example galleries)
Under The Hood
Architecture
The ExpressiveCode class (packages/expressive-code/src/index.ts) extends ExpressiveCodeEngine (packages/@expressive-code/core/src/common/engine.ts), which owns a layered render pipeline split across internal/render-group.ts, render-block.ts, and render-line.ts: block-level annotations resolve first, then each line is rendered and hast-transformed. Extensibility runs entirely through plugin hooks - ExpressiveCodePlugin objects register a hooks map (e.g. postprocessAnnotations in internal/core-plugins.ts) that the engine invokes at fixed pipeline phases, an explicit hook contract rather than subclassing. The @expressive-code/core package itself depends on no specific plugin; this bundle package composes Shiki, Frames, and Text Markers together in its constructor, prepending each unless disabled or already present in the caller’s plugin list. Because every official integration (astro-expressive-code, rehype-expressive-code, remark-expressive-code) depends on the same render pipeline and hook contract, a change to the core engine or renderer propagates to all of them at once.
Tech Stack
Strict TypeScript across the monorepo (tsconfig.base.json enables strict and exactOptionalPropertyTypes). AST construction and querying goes through the unified/rehype ecosystem - hastscript, hast-util-to-html, hast-util-select, hast-util-to-text, unist-util-visit(-parents) - while theme/color processing uses @ctrl/tinycolor, culori, and postcss/postcss-nested for contrast-aware CSS variable generation. Syntax highlighting is delegated entirely to Shiki, the same TextMate-grammar engine VS Code uses. Each package builds independently via tsup (esbuild-based) across a pnpm workspace (packages/@expressive-code/*, packages/*, internal/*), with Changesets managing versioning and release notes.
Code Quality
The core package alone ships 12 dedicated Vitest suites (block, engine, render-line, plugin-hooks, plugin-data, theme, css, color-transforms, i18n, line, meta-options, objects), several of which systematically probe invalid-argument handling (e.g. block.test.ts asserts constructor throws across non-string/number/object inputs) rather than only covering happy paths. ESLint, Prettier, and markdownlint all run in CI (.github/workflows/ci.yml) alongside the test suite. Error handling favors explicit thrown Errors with migration guidance over silent fallbacks - the textMarkers/frames config-shape checks in expressive-code’s constructor are one example. No gaps in test coverage were evident during this review.
What Makes It Unique
Rather than being a syntax highlighter with markup bolted on, the package’s real contribution is a uniform hook-driven annotation model: highlighted lines, inline text markers, diffs, editor frames, and even indentation handling (the built-in “Indent wrapper” core plugin) are all implemented as ExpressiveCodeAnnotation objects processed through the identical hast-transformation contract that third-party plugins (line numbers, collapsible sections) use. Combined with automatic minimum-contrast color adjustment across multiple simultaneous themes and framework-agnostic HAST output, it addresses a narrower but genuinely underserved problem - consistent, accessible, multi-theme code annotation - that general-purpose highlighters like Shiki or Prism leave entirely to the consuming application.
Used by 5 apps in this directory
likec4
Developer Tools · Devops
Define your software architecture as code and get always up-to-date, interactive C4 diagrams generated automatically from a DSL.
Operational
Analytics · Automation · Monitoring
Open-source event tracker with real-time push notifications and action buttons for tech founders.
Papra
Bookmarks Archiving
Self-hosted document archiving with email ingestion, OCR full-text search, and pluggable storage — store once, find anything.
Sentry
Security · Developer Tools · Monitoring
Developer-first error tracking and performance monitoring platform with AI-powered root-cause analysis across 20+ languages and frameworks.
Terrateam
Devops · Developer Tools
GitOps automation for Terraform, Pulumi, and IaC at scale