mrkeyoor.com_
Sat 08 Aug 21:58 UTC
npmWeb Frontendupdated 08 Aug 2026

html-dom-parser

html-dom-parser converts an HTML string into domhandler node objects on both Node and browsers. On the server, version 8 wraps htmlparser2 and removes the synthetic root parent. In a browser bundle, it parses through native DOM APIs and converts the result back into matching Element, Text, Comment, and ProcessingInstruction objects. The output preserves parent, previous-sibling, next-sibling, children, tag-name, and attribute links, making it a low-level bridge for tools that want one traversable tree shape in both environments.

Verdict

A solid compatibility layer when domhandler nodes are the required interchange format across Node and browsers. Use htmlparser2 directly for server-only work, and add an explicit sanitizer before any untrusted tree reaches rendered DOM.

API stability3/5The default parse function and domhandler node model are straightforward, but each recent major follows major upgrades in domhandler or htmlparser2. The README records removed parser options in v3, a CommonJS .default migration in v5, removed client exports in v6, dependency upgrades in v7, and an ES6 output target in v8. Call sites are simple, yet packaging and node-class compatibility need attention at every major.
Docs4/5The README shows exact tree output, ESM and CommonJS imports, fragment and attribute parsing, browser Trusted Types setup, the complete server option interface, client limitations, SVG casing notes, and a migration section from v1 through v8. It could be clearer that returned trees are cyclic, that parsing does not sanitize, and that native browser parsing may not exactly match htmlparser2 on malformed markup.
Maintenance5/5Version 8.0.0 was published on 2026-05-25, and GitHub reports a push on 2026-08-07, one day before this guide's date. The repository is not archived and reports four open issues and PRs. Automated release tooling, current TypeScript, browser tests, package-publication checks, and explicit migrations for upstream parser majors all show active ownership rather than a dormant compatibility wrapper.
Ecosystem4/5The npm downloads endpoint reports 3,818,240 downloads in its last-week window. Direct GitHub interest is smaller at 108 stars, but the package is built on htmlparser2 12 and domhandler 6, so its node classes fit a well-established parsing ecosystem. Its browser bridge is especially useful to packages that need those server-style nodes, though ordinary DOM users may prefer native APIs.

Use it if

  • You need domhandler-compatible nodes from HTML fragments in both Node and browser bundles
  • You are building or maintaining tooling around html-react-parser, htmlparser2, or another package that already expects domhandler nodes
  • Server code needs htmlparser2 options such as xmlMode, decodeEntities, or case-control flags
  • A browser with Trusted Types enforcement needs a caller-supplied policy before the parser assigns HTML to a template or document
Skip it if

Setup reality

npm install html-dom-parser installs two pinned runtime dependencies: domhandler 6.0.1 and htmlparser2 12.0.0. There are no peer dependencies, native builds, credentials, or project config. Version 8 publishes ESM, CommonJS, browser-conditional, and React Native mappings plus TypeScript declarations. ESM uses a default import, but CommonJS must use require('html-dom-parser').default; v5 introduced that .default requirement and old examples without it fail with a not-a-function error. Runtime selection is done by package conditions and browser mappings. Node receives the htmlparser2 implementation and can use parser and handler options. Browser bundlers receive the client implementation, which uses template.innerHTML, createHTMLDocument, or DOMParser and accepts only trustedTypePolicy as a meaningful option. That means malformed markup, implied elements, tag casing, and document wrappers can differ between server and browser native parsing, so hydration-sensitive output needs cross-runtime tests. Parsing an empty string returns an empty array, while a non-string throws TypeError. Results are cyclic: child.parent points upward and siblings link through prev and next, so JSON.stringify on a tree throws unless you strip links or use a cycle-aware serializer. Parsing is not sanitizing. A Trusted Types policy only changes what gets assigned to innerHTML; its createHTML callback must apply a real sanitizer if input is untrusted. Server options such as xmlMode and decodeEntities can also change tree meaning, and the browser path cannot mirror them. Version 8 targets ES6, so legacy browser transpilation is the application's responsibility.

Patterns

Parse an HTML fragmentparse-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 function returns an array because a fragment can contain multiple root nodes, including text and comments.

Load version 8 from CommonJSrequire-commonjs

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

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

CommonJS callers need .default. The migration guide says this changed in v5.

Handle an empty fragmenthandle-empty-input

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

An empty string returns an empty array. A non-string value throws TypeError instead of returning an empty result.

Recursively walk elements and textwalk-node-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);

domhandler nodes also contain parent, prev, and next links. Use children for a downward traversal and avoid revisiting parent links.

Read parsed attributesread-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']);
}

Attributes are plain strings in an attribs object. Parsing does not validate URLs or remove event-handler attributes.

Parse XML-style markup on the serverparse-xml-mode

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

These htmlparser2 options apply to the Node server parser. Browser parsing uses native DOM behavior and cannot mirror the option set.

Keep encoded entities on the serverpreserve-entity-text

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

console.log(text.data); // Tom &amp; Jerry

decodeEntities is a server option. Browser DOM parsing decodes according to the browser and ignores this setting.

Preserve server-side XML tag casecontrol-tag-case

const [node] = parse('<LinearGradient />', {
  xmlMode: true,
  lowerCaseTags: false,
});

console.log(node.type === 'tag' && node.name); // LinearGradient

HTML mode normally lowercases tags. Browser parsing follows HTML DOM rules and has its own SVG case-name mapping.

Sanitize through a browser Trusted Types policyuse-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 a separate dependency. A Trusted Types policy that simply returns input would satisfy the API but would not sanitize anything.

Validate the input boundaryreject-non-string-input

function parseMarkup(value) {
  if (typeof value !== 'string') {
    throw new TypeError('markup must be a string');
  }
  return parse(value);
}

Version 8 throws TypeError when the first argument is not a string on both server and browser paths.

Project nodes into JSON-safe datastrip-cycles-for-json

function toJSONSafe(node) {
  if (node.type === 'text' || node.type === 'comment') {
    return { type: node.type, data: node.data };
  }
  return {
    type: node.type,
    name: 'name' in node ? node.name : undefined,
    attributes: 'attribs' in node ? node.attribs : undefined,
    children: 'children' in node ? node.children.map(toJSONSafe) : [],
  };
}

const safe = parse('<p>Hello</p>').map(toJSONSafe);

Direct JSON.stringify fails on normal element trees because child.parent points back to its parent. Project only the fields you need.

Collect matching elements without a selector enginefind-elements-by-name

function findAll(nodes, name, found = []) {
  for (const node of nodes) {
    if (node.type === 'tag' && node.name === name) found.push(node);
    if ('children' in node) findAll(node.children, name, found);
  }
  return found;
}

const links = findAll(parse(html), 'a');

html-dom-parser supplies a tree, not CSS selectors. Use Cheerio if selection and mutation are the main job.

Alternatives

PackageRegistryPick it when
htmlparser2npmNode-only parsing where you want the underlying streaming parser, parseDOM API, and direct option control
parse5npmYou need specification-oriented HTML document parsing, serialization, or source location information
linkedomnpmServer code needs browser-like Document and DOM APIs rather than domhandler nodes
cheerionpmYou want jQuery-style selection and mutation on server-parsed markup instead of manual tree traversal