mrkeyoor.com_
Wed 23 Sept 00:33 UTC
npmWeb Frontendupdated 22 Sept 2026

html-dom-parser review

Html-dom-parser 8.0.2 converts an HTML string into domhandler nodes in Node and browsers, and our browser build measured 6.2 KB minified. The server entry wraps htmlparser2's parseDOM and removes its synthetic root parent. Browser builds select a different entry that parses with native DOM APIs, then converts native nodes into matching Element, Text, Comment, and ProcessingInstruction objects. The result includes children, parent, previous-sibling, next-sibling, tag-name, and attribute links. Version 8 targets ES6 instead of ES5; patch 8.0.2 bundles ESM-only server dependencies into its CommonJS output to fix ERR_REQUIRE_ESM_RACE_CONDITION. This is a tree adapter, not an HTML sanitizer, selector engine, or serializer.

Verdict

Html-dom-parser 8.0.2 installed in 0.8 seconds, occupied 2 MB across 7 packages, and bundled to 2.5 KB gzipped in our sandbox. Install it when domhandler nodes must cross Node and browser code; use htmlparser2 directly for server-only parsing, and sanitize untrusted input separately.

We installed it

Lab card: what happened when we installed html-dom-parserScreenshot of html-dom-parser documentation
Install✓ · 0.8s7 packages on disk · 2 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser2.5 KBgzipped (6.2 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does html-dom-parser install cleanly?

Yes. In a fresh container with an empty cache, npm install html-dom-parser finished in 0.8s, leaving 7 packages and 2 MB on disk. npm audit reported no known vulnerabilities.

How much does html-dom-parser add to a browser bundle?

2.5 KB gzipped (6.2 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does html-dom-parser work with both ESM and CommonJS?

Yes. Both import 'html-dom-parser' and require('html-dom-parser') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does html-dom-parser include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

html-dom-parser or htmlparser2: which should you use?

htmlparser2: Use it for Node-only parsing when you want the underlying streaming parser, parseDOM, and direct control over options. Html-dom-parser 8.0.2 installed in 0.8 seconds, occupied 2 MB across 7 packages, and bundled to 2.5 KB gzipped in our sandbox.

When should you not use html-dom-parser?

Untrusted HTML must become safe to render. Parsing preserves tags and attributes, and the README's Trusted Types example calls a separate DOMPurify sanitizer.

API stability3/5The callable default export and domhandler tree shape keep usage simple, but the migration section lists meaningful changes in most recent majors. Version 3 removed parser options, v5 changed CommonJS calls to require(...).default, v6 removed client exports, v7 upgraded htmlparser2, and v8 changed emitted JavaScript from ES5 to ES6. Patch 8.0.2 then repaired CommonJS packaging around ESM-only dependencies. Call sites are short, yet module and node-class compatibility need review at each major.
Docs4/5The README shows the returned node graph, ESM and CommonJS imports, fragments, attributes, empty input, browser Trusted Types, every exposed htmlparser2 server option, SVG case behavior, and migration notes from v1 through v8. It also states that browser options cannot mirror the server. The guide leaves several operational conclusions implicit: trees are cyclic, a Trusted Types policy is not itself sanitization, and malformed HTML can produce different native-browser and htmlparser2 trees.
Maintenance5/5npm published 8.0.2 on 2026-08-13 with a named fix for ERR_REQUIRE_ESM_RACE_CONDITION, and GitHub recorded another push on 2026-08-25. The unarchived repository has 108 stars and reports 2 open issues and pull requests. Release automation and frequent compatibility work against domhandler and htmlparser2 show active ownership. That work is important here because conditional browser, ESM, and CommonJS entries create more packaging paths than the one-function API suggests.
Ecosystem4/5The npm endpoint counted 4,070,958 downloads in the latest completed week. Its output uses domhandler 6.0.1 and its server path uses htmlparser2 12.0.0, so trees plug into an established parser family and packages such as html-react-parser. The browser adapter is the differentiator. Projects using ordinary native DOM nodes, selectors, or document serialization may gain little from converting into domhandler's cyclic graph.

Use it if

  • Your code needs domhandler-compatible nodes from HTML fragments in both Node and browser bundles.
  • A tool already exchanges trees with html-react-parser, htmlparser2, or another domhandler consumer.
  • Server parsing needs htmlparser2 switches such as xmlMode, decodeEntities, or tag-case controls.
  • A browser under Trusted Types enforcement must pass a caller-created policy before assigning parsed markup.
Skip it if

Setup reality

We installed html-dom-parser 8.0.2 in a fresh unprivileged Node 22 Bookworm sandbox. npm finished in 0.8 seconds and left 7 packages using 2 MB on disk. The package is 820 KB unpacked, with 2 direct dependencies and 0 peers. npm audit found 0 known vulnerabilities. Both require() and ESM import worked through the exports map. Our inspection found no TypeScript types in the installed package. The browser build measured 6.2 KB minified and 2.5 KB gzipped.

No credentials, native compilation, or project config are needed. ESM uses the default import. CommonJS callers still need require('html-dom-parser').default, a change documented since v5. Version 8.0.2 specifically repairs a CommonJS failure involving ESM-only server dependencies, so projects pinned to 8.0.0 or 8.0.1 should take the patch. The runtime selected by the package conditions changes the parser underneath the same top-level function.

On Node, options pass through to htmlparser2 and can change entity decoding, XML behavior, CDATA recognition, self-closing tags, and case handling. In a browser, native DOM parsing takes over and the README says those controls are unavailable; trustedTypePolicy is the meaningful browser option. Test malformed markup and SVG casing in both runtimes if server rendering must hydrate into a browser result. An empty string returns an empty array, while a non-string throws TypeError.

Returned trees are cyclic because every child points back to its parent and siblings link through prev and next. Direct JSON.stringify therefore fails on ordinary element trees; project the fields you need or use a cycle-aware serializer. A Trusted Types policy controls the innerHTML assignment but does not clean anything unless createHTML calls a sanitizer. Server xmlMode can also change tree meaning in a way the browser path cannot reproduce.

Patterns

Parse a fragment into root nodes parse-html-fragment

import parse from 'html-dom-parser';

const nodes = parse('<p class="lead">Hello <em>there</em></p>');
const paragraph = nodes[0];
console.log(paragraph.type, paragraph.name, paragraph.attribs.class);

The result is always an array because one fragment can contain several root elements, text nodes, or comments.

Load the CommonJS entry correctly load-commonjs-default

const parse = require('html-dom-parser').default;

const nodes = parse('<strong>Important</strong>');

CommonJS requires .default. The migration notes date that interop requirement to v5, and 8.0.2 fixes a separate CommonJS dependency race.

Handle empty HTML handle-empty-fragment

const nodes = parse('');
console.log(nodes.length); // 0

An empty string returns an empty array. Passing null, an object, or another non-string value throws TypeError.

Walk text and element children walk-domhandler-tree

function walk(node) {
  if (node.type === 'text') console.log(node.data);
  if ('children' in node) {
    for (const child of node.children) walk(child);
  }
}

for (const node of parse('<p>Hello <em>there</em></p>')) walk(node);

Traverse children downward. Following parent, prev, and next without a visited set can loop through the cyclic graph.

Read element attributes read-element-attributes

const [link] = parse('<a href="/docs" data-kind="local">Docs</a>');

if (link.type === 'tag') {
  console.log(link.attribs.href, link.attribs['data-kind']);
}

Attribute values remain strings. Parsing does not validate href values or remove event-handler attributes.

Enable XML rules on Node parse-xml-on-server

const nodes = parse('<feed><entry /></feed>', {
  xmlMode: true,
  recognizeSelfClosing: true,
});

These htmlparser2 settings apply to the Node path. Browser builds use native DOM parsing and cannot honor the same option set.

Keep entity text encoded preserve-encoded-entities

const [text] = parse('Tom &amp; Jerry', {
  decodeEntities: false,
});
console.log(text.data);

decodeEntities is server-only. A browser decodes according to its DOM parser and ignores this option.

Keep XML tag casing preserve-xml-tag-case

const [node] = parse('<LinearGradient />', {
  xmlMode: true,
  lowerCaseTags: false,
});
console.log(node.type === 'tag' && node.name);

HTML mode normally lowercases tag names. Browser SVG handling follows native rules and may not match this Node configuration.

Sanitize through a Trusted Types policy apply-trusted-types-policy

import DOMPurify from 'dompurify';
import parse from 'html-dom-parser';

const policy = window.trustedTypes?.createPolicy('parsed-html', {
  createHTML(input) {
    return DOMPurify.sanitize(input);
  },
});
const nodes = parse(untrustedHTML, { trustedTypePolicy: policy });

DOMPurify is separate. A policy that returns its input unchanged satisfies Trusted Types but still leaves unsafe markup intact.

Remove cycles before JSON serialization serialize-tree-safely

function plain(node) {
  if (node.type === 'text' || node.type === 'comment') {
    return { type: node.type, data: node.data };
  }
  return {
    type: node.type,
    name: node.name,
    attributes: node.attribs,
    children: node.children?.map(plain) || [],
  };
}
const json = JSON.stringify(parse('<p>Hello</p>').map(plain));

Direct JSON.stringify sees child.parent and throws on the circular reference. Copy only the downward fields needed by the consumer.

Alternatives

PackageRegistryPick it when
htmlparser2npmUse it for Node-only parsing when you want the underlying streaming parser, parseDOM, and direct control over options.
parse5npmUse it for specification-focused document parsing, serialization, or optional source-location data.
node-html-parsernpmUse it when server code wants a higher-level DOM-like API without matching domhandler's exact node classes.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.