hast-util-to-html
Serializes hast syntax trees into WHATWG-compliant HTML strings, with fine-grained control over escaping, quoting, and tag omission.
Repository Health
Technical Analysis
hast-util-to-html is the canonical HTML serializer for the unified/hast ecosystem, turning a hast syntax tree back into a WHATWG-compliant HTML string. It underlies rehype-stringify, the terminal stringifier used by nearly every rehype and remark-to-HTML pipeline, which is why it sits at extremely high npm download volume despite a small, focused API surface.
Beyond straightforward serialization, it exposes a large set of formatting options — omitting optional tags, tightening attribute whitespace, smart-quoting, self-closing SVG elements, and configuring character-reference encoding — so the same tree can be rendered as readable markup for humans or minified output for production bundles, all while automatically toggling between HTML and SVG serialization rules as it walks the tree.
What You Get
- A single
toHtml(tree, options)export with no default export, fully typed via JSDoc-derived TypeScript declarations - Automatic HTML/SVG namespace switching when the tree crosses an
<svg>boundary, so attribute casing and void-element rules stay correct - Over a dozen formatting knobs (quoting, tag omission, tight attributes, doctype casing) for producing either readable or byte-minimal output
- Configurable character-reference and XSS-safety controls (
allowDangerousHtml,allowDangerousCharacters) that default to safe encoding unless explicitly disabled
Common Use Cases
- Serializing a processed hast tree back to HTML at the end of a rehype pipeline, typically via
rehype-stringify - Building HTML minifiers or pretty-printers on top of hast by toggling the tight/omission options
- Round-tripping HTML through
hast-util-from-html→ transform →hast-util-to-html - Re-emitting sanitized (via
hast-util-sanitize) user content back to safe HTML strings
Under The Hood
Architecture
The library is a thin dispatch layer over a zwitch-based visitor: lib/handle/index.js builds a zwitch('type', ...) switch keyed on hast node type (comment, doctype, element, raw, root, text), each handled by its own small file in lib/handle/. lib/index.js owns the public toHtml() entry point, builds a shared State object (settings, current quote/alternative quote, active schema from property-information) and exposes one/all recursion helpers that the individual handlers call back into; lib/handle/element.js is the largest handler (~265 lines) because it owns attribute serialization, self-closing/void-element logic, and the HTML↔SVG schema swap on <svg> boundaries. Optional-tag omission is fully isolated into lib/omission/ (opening.js, closing.js, omission.js), which element.js consults only when omitOptionalTags is set — a clean separation between “always correct” serialization and “legal but aggressive” minification behavior.
Tech Stack
Plain ESM JavaScript ("type": "module") with zero build step for runtime — types ship via a tsc --build pass emitting .d.ts/.d.ts.map declaration files from JSDoc comments, checked with type-coverage (configured for 100% strict coverage in package.json). Runtime dependencies are all narrowly-scoped unified-ecosystem utilities: property-information (attribute/schema knowledge), stringify-entities (character-reference encoding), zwitch (type-based dispatch), plus small helpers (ccount, comma-separated-tokens, space-separated-tokens, html-void-elements, hast-util-whitespace). Linting/formatting run through xo (ESLint config) and prettier; CI (.github/workflows/main.yml) runs the test suite across two Node LTS lines and uploads coverage to Codecov.
Code Quality
Testing is extensive and enforced strictly: test/ contains 30+ files, with dedicated suites per node type (element.js, comment.js, doctype.js, text.js, raw.js, root.js) plus an entire subset (omission-*.js, 20+ files) exercising every individual optional-tag-omission rule (e.g. omission-closing-tbody.js, omission-opening-html.js) and a dedicated security.js suite for XSS-relevant escaping behavior. The test npm script chains build (typecheck + type-coverage) → format (remark/prettier/xo —fix) → test-coverage, and test-coverage runs c8 --100, meaning the project fails CI on anything less than 100% statement coverage. This is a notably higher bar than most npm packages in this size class.
API Design
The public surface is intentionally minimal — one function, one options object, no classes or default export — which keeps the getting-started cost close to zero (import {toHtml} from 'hast-util-to-html'; toHtml(tree)). Every option is documented inline via JSDoc @property tags with defaults spelled out, and the README mirrors that same structure field-by-field, so IDE tooltips and the docs site stay in lockstep. The main ergonomic cost is the sheer number of formatting options (15+), most of which exist purely to support minifier/pretty-printer use cases rather than everyday serialization, but sensible false defaults mean the common case needs no configuration at all.
Used by 8 apps in this directory
Harness Open Source
Developer Tools · Devops · Code Editors
A unified open source DevOps platform combining Git hosting, CI/CD pipelines, cloud development environments, and artifact registries in a single self-hosted system.
Multica
AI Assistants · AI Development
Turn coding agents into real teammates — assign issues, track progress, and compound reusable skills across a vendor-neutral, self-hosted platform.
Notifuse
Marketing
Open-source, self-hosted alternative to Mailchimp, Brevo, and Klaviyo — send newsletters and transactional emails without per-email pricing or vendor lock-in.
Novu
Developer Tools
Open-source communication infrastructure that connects your products and AI agents to every channel your users live on — Inbox, Email, SMS, Push, Chat, and more.
Orama
Search · Developer Tools
A complete, embeddable search engine and RAG pipeline running in browsers, servers, and edge networks with full-text, vector, and hybrid search in under 2KB.
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.
Supabase
Developer Tools · Databases · Search
The open-source Postgres development platform that replaces Firebase with authentication, real-time APIs, edge functions, storage, and vector embeddings — all built on PostgreSQL.
Zulip
Team Chat
Topic-based team chat that brings the structure of email threads to real-time messaging, so distributed teams never lose context across hundreds of concurrent conversations.