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

html-react-parser

html-react-parser turns an HTML string into React elements on either Node.js or a browser. It parses the markup into domhandler nodes, converts normal attributes and inline styles into React props, and lets you replace, remove, or transform nodes during conversion. It is useful when HTML comes from a CMS or stored rich-text field and selected tags must become application components. It is a converter, not an HTML sanitizer, Markdown renderer, template engine, or browser security boundary.

Verdict

A well-maintained and flexible converter for trusted or already-sanitized HTML, especially when tags must become real React components. Do not install it as an XSS defense, and avoid it for static content that should simply be JSX.

API stability4/5The core parse(string, options) shape and replace callback have survived multiple major releases, and the package supports both ESM and CommonJS exports. Major upgrades still carry concrete work: version 5 moved the codebase to TypeScript and changed CommonJS access to .default, while version 6 raised the output target to ES2016 and upgraded its parser node dependencies. The migration section records these breaks clearly, but consumers that inspect domhandler nodes are exposed to dependency type changes.
Docs5/5The README covers basic parsing, replacement, child preservation, attribute conversion, removal, transforms, Preact integration, server-only parser options, whitespace, Trusted Types, every major migration, and TypeScript narrowing. Its FAQ directly states that parsing is not XSS-safe, invalid HTML is not sanitized, inline handlers stay strings, and malformed self-closing tags can change nesting. Runnable StackBlitz, TypeScript, JSFiddle, and repository examples make the documented behavior testable.
Maintenance5/5Version 6.1.5 was published on July 17, 2026, the repository was pushed on August 7, 2026, and GitHub reports 13 open issues and pull requests rather than an abandoned queue. The project runs a GitHub Actions build, publishes benchmark and size-limit commands, maintains migration notes across six major lines, and keeps current parser dependencies. That is strong evidence of active compatibility work rather than download volume alone.
Ecosystem4/5The measured week recorded 3,824,751 downloads, React peer ranges extend through React 19, and the API can target Preact or a custom element library. It composes with domhandler, htmlparser2 settings, Trusted Types, DOMPurify, and other sanitizers, while ESM and CommonJS builds cover common toolchains. It is still a specialized bridge rather than a full content pipeline, so unified and rehype have a broader plugin ecosystem for syntax trees, Markdown, and structured transformations.

Use it if

  • You receive trusted or separately sanitized HTML from a CMS and need React elements instead of dangerouslySetInnerHTML
  • You need to replace selected HTML tags with application components while preserving their parsed children
  • You render the same stored HTML during Node.js SSR and browser hydration and want one conversion API
  • You need TypeScript access to parsed DOM nodes, React prop conversion, and a post-conversion transform hook
Skip it if

Setup reality

Install html-react-parser alongside React; version 6.1.5 declares React and @types/react peers covering React 0.14 through 19. ESM uses a default import, while CommonJS must read require('html-react-parser').default, a small packaging detail that causes real migration failures. The package includes declarations, but replace receives a DOMNode union, so TypeScript code must narrow with domNode instanceof Element before reading name, attribs, or children. Nothing in the install sanitizes input. Run untrusted HTML through DOMPurify, sanitize-html, or an equivalent policy before parsing, and configure that sanitizer for your threat model. A Trusted Types policy can be passed in browsers, but its createHTML function still needs to do the actual sanitization. Server-specific htmlparser2 options such as xmlMode do not apply in browsers and can make SSR output disagree with hydration. Whitespace is preserved by default; trim removes it globally and may also remove intentional spaces inside elements. Version 6 targets ES2016, so old bundles may need transpilation. Static markup should remain JSX because parsing on every render adds work and makes component boundaries harder to inspect. For repeated content, sanitize and parse when the source changes, then memoize the result rather than rebuilding the tree on unrelated renders.

Patterns

Convert an HTML string to React elementsparse-basic-html

import parse from 'html-react-parser';

export function ArticleIntro({ html }: { html: string }) {
  return <section>{parse(html)}</section>;
}

Only do this with trusted or separately sanitized HTML. Parsing does not make hostile markup safe.

Render adjacent parsed elements under a parentrender-multiple-roots

import parse from 'html-react-parser';

const items = '<li>First</li><li>Second</li>';

export function List() {
  return <ul>{parse(items)}</ul>;
}

Multiple top-level nodes produce an array. Put that result under a valid React parent for the intended document structure.

Replace a custom tag with an application componentreplace-tag-with-component

import parse, { Element, type HTMLReactParserOptions } from 'html-react-parser';
import { ProductCard } from './ProductCard';

const options: HTMLReactParserOptions = {
  replace(node) {
    if (node instanceof Element && node.name === 'product-card') {
      return <ProductCard id={node.attribs['data-id']} />;
    }
  },
};

const content = parse(html, options);

Narrow DOMNode with instanceof Element before reading tag-specific properties in TypeScript. Validate data-id before passing it into sensitive application logic.

Replace a wrapper and keep its childrenpreserve-replaced-children

import parse, { domToReact, Element, type DOMNode, type HTMLReactParserOptions } from 'html-react-parser';

const options: HTMLReactParserOptions = {
  replace(node) {
    if (node instanceof Element && node.name === 'callout') {
      return <aside className="callout">{domToReact(node.children as DOMNode[], options)}</aside>;
    }
  },
};

parse('<callout><strong>Heads up</strong></callout>', options);

Pass the same options into domToReact when nested custom tags should receive the same replacements.

Carry HTML attributes onto a replacementconvert-html-attributes

import parse, { attributesToProps, Element } from 'html-react-parser';

parse('<main class="article" style="text-align:center"></main>', {
  replace(node) {
    if (node instanceof Element && node.name === 'main') {
      return <section {...attributesToProps(node.attribs)} />;
    }
  },
});

attributesToProps converts names such as class and parses inline style into React-compatible props. Filter unwanted attributes before spreading untrusted data.

Remove selected nodes while parsingremove-selected-elements

import { Fragment } from 'react';
import parse, { Element } from 'html-react-parser';

const rendered = parse(html, {
  replace(node) {
    if (node instanceof Element && ['script', 'iframe'].includes(node.name)) {
      return <Fragment />;
    }
  },
});

This is a display transformation, not a sanitizer. A denylist misses dangerous attributes, protocols, SVG, malformed markup, and future cases.

Transform elements after conversiontransform-react-elements

import { isValidElement } from 'react';
import parse from 'html-react-parser';

const rendered = parse(html, {
  transform(reactNode, domNode, index) {
    return isValidElement(reactNode) && reactNode.type === 'img'
      ? <figure key={index}>{reactNode}</figure>
      : reactNode;
  },
});

The traversal index restarts within child lists, so the README warns not to treat it as a globally unique key.

Sanitize browser HTML before conversionsanitize-before-parsing

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

export function SafeRichText({ html }: { html: string }) {
  const clean = DOMPurify.sanitize(html, {
    USE_PROFILES: { html: true },
  });
  return <div>{parse(clean)}</div>;
}

DOMPurify does the security work here. Configure its allowlist for the content you accept, and use its supported server setup if sanitizing during SSR.

Pass a sanitizing Trusted Types policyuse-trusted-types-policy

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

const policy = window.trustedTypes?.createPolicy('rich-text', {
  createHTML(input) {
    return DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: false });
  },
});

const rendered = parse(html, { trustedTypePolicy: policy });

Trusted Types enforcement does not sanitize by itself. The policy callback must return cleaned HTML, and this browser API needs a window guard in SSR code.

Drop formatting whitespacetrim-formatting-whitespace

import parse from 'html-react-parser';

const compact = parse('<p>One</p>
<p>Two</p>', { trim: true });

trim is global and can remove meaningful content such as the single space in <p> </p>. Use it only when that loss is acceptable.

Enable XML mode in Node.jsparse-xml-on-server

import parse from 'html-react-parser';

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

htmlparser2 options work only on the server. Do not use different parsing rules for markup that will hydrate in a browser.

Use Preact as the element libraryrender-with-preact

import * as preact from 'preact';
import parse from 'html-react-parser';

const vnode = parse('<strong>Small app</strong>', {
  library: preact,
});

The supplied library must expose compatible createElement, cloneElement, and isValidElement functions. React remains a declared peer of the package.

Alternatives

PackageRegistryPick it when
rehype-reactnpmChoose it when content already flows through unified or rehype and you want AST plugins before React conversion.
dompurifynpmChoose it when the actual requirement is browser-side XSS sanitization; use its cleaned HTML with a renderer afterward.
sanitize-htmlnpmChoose it for a server-friendly allowlist sanitizer with per-tag attribute controls before rendering stored HTML.