dom-serializer

Renders domhandler DOM nodes back into HTML or XML strings, with fine control over entity encoding and tag formatting.

Library
npm
v3.1.1
148stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
78/100Good
Development Activity96
Maintenance72
Community72
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture85
Code Quality88
Innovation75
Learning Curve45

dom-serializer turns a domhandler DOM node (or array of nodes) back into a markup string — effectively the outerHTML of whatever tree you hand it. It is the write-side counterpart to htmlparser2’s parser: parse markup into a domhandler tree, mutate it however you like, then serialize it back out with this package.

It underpins cheerio’s .html() and .toString() output, so its behavior is exercised by every cheerio consumer even when they never import it directly. The API is a single render() function with an options object covering entity encoding, self-closing tags, XML mode, and empty-attribute formatting, letting callers reproduce either strict HTML or XML serialization semantics from the same DOM representation.

What You Get

  • A single render(node, options) function that serializes one node or an array of nodes to a markup string
  • Configurable entity encoding (encodeEntities/decodeEntities) with a documented round-trip mode for parsers that don’t decode entities on input
  • XML mode (xmlMode) for self-closing tags and empty-attribute printing, plus a "foreign" mode that restores mixed-case SVG/MathML element and attribute names
  • Correct handling of HTML void elements, CDATA sections, comments, and processing instructions
  • Full TypeScript types for DomSerializerOptions shipped alongside the compiled output

Common Use Cases

  • Serializing a cheerio-manipulated DOM back to an HTML string after scraping or templating edits
  • Round-tripping HTML or XML through htmlparser2 (parse) and dom-serializer (serialize) without corrupting entity references
  • Rendering SVG or MathML fragments that require foreign-content name casing to be preserved
  • Building custom HTML/XML processing tools on top of domhandler’s DOM representation without pulling in a full browser DOM

Under The Hood

Architecture The package is a small, function-first module with no classes: render() wraps a single node or array into a list and delegates to a recursive renderNode()/renderChildren() pair that switches on domelementtype’s node-type enum (Root, Directive, Comment, CDATA, Tag/Script/Style, Text). xmlMode is threaded as a plain parameter rather than mutating a shared options object, so entering and leaving SVG/MathML “foreign” mode during tag rendering (renderTag in src/index.ts) doesn’t require copying options at each recursion level. Attribute serialization is isolated in formatAttributes, which picks an encoder function once per tag rather than branching per attribute.

Tech Stack Written in TypeScript, published as ESM-only ("type": "module") targeting Node >=20.19.0, with domelementtype, domhandler, and entities as the only runtime dependencies. The build is a plain tsc compile; linting layers ESLint (via @feedic/eslint-config and typescript-eslint) on top of Biome for formatting and additional static checks, and tests run under Vitest.

Code Quality The test suite (src/index.spec.ts) is behavioral rather than unit-level: it loads markup through cheerio/htmlparser2 and asserts on the serialized round-trip output, covering quote-escaping edge cases, entity encoding modes, self-closing tag variants, XML mode, and non-string attribute values. A companion index.bench.ts benchmarks serialization performance. Types are strict throughout, JSDoc comments document every option’s default and security implications (notably around disabling entity encoding), and GitHub Actions CI (nodejs-test.yml) runs the suite on each push. No dedicated unit tests target the internal helper functions in isolation, but the integration-style coverage exercises them indirectly through realistic markup.

API Design The public surface is deliberately minimal — one default-exported render() function plus a single options interface — and the option names (decodeEntities, xmlMode) are chosen to mirror htmlparser2’s parser options so a single options object can be threaded through both parse and serialize calls for a faithful round trip. This is a standard, well-documented ergonomic choice rather than a novel technical approach; the library’s value is in doing HTML/XML serialization edge cases correctly, not in API novelty.

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