html-react-parser turns an HTML string into real React elements instead of using dangerouslySetInnerHTML, but it is a parser, not a sanitizer, so feeding it untrusted HTML is a cross-site scripting risk you have to close yourself. The library does a genuinely useful job. The mistake teams make is assuming that because it avoids dangerouslySetInnerHTML, it also avoids XSS. It does not, and the maintainers say so directly.
If you have reached for html react parser to render server-supplied markup, this is the piece worth reading before it hits production.
What the Library Does
The problem it solves is common: you have an HTML string, maybe from a CMS, a markdown renderer, or a legacy API, and you want it to become part of your React tree rather than a raw innerHTML blob. The naive approach is dangerouslySetInnerHTML, which drops out of React's reconciliation and hands you a well-known XSS footgun.
html-react-parser instead parses the string and returns React elements:
import parse from "html-react-parser";
function Article({ body }) {
// body is an HTML string like "<p>Hello <strong>world</strong></p>"
return <div className="prose">{parse(body)}</div>;
}
The output is a proper React node, so it participates in the virtual DOM, keys work, and you can even transform nodes as they are parsed. That transform hook is the library's best feature. You can rewrite an <a> into your router's Link, swap <img> for a lazy-loading component, or strip attributes you do not want:
import parse, { domToReact } from "html-react-parser";
const options = {
replace(node) {
if (node.type === "tag" && node.name === "a") {
return (
<a href={node.attribs.href} rel="noopener noreferrer" target="_blank">
{domToReact(node.children, options)}
</a>
);
}
},
};
parse(body, options);
This is why the package is popular, pulling millions of weekly downloads, and why it is actively maintained rather than abandoned. It is a well-built tool.
The Security Reality: It Is Not XSS-Safe
Here is the part the README is honest about and users routinely miss. html-react-parser is not equipped to prevent XSS. It faithfully turns whatever HTML you give it into React elements. If the input string contains an event-handler attribute or a javascript: URL, the parser is happy to reproduce that structure. It is doing exactly what it advertises: parsing HTML. It was never a security control.
React's own escaping protects you from XSS when you render text as children, because React escapes string children by default. But an HTML parser's whole job is to interpret markup as markup, which is precisely the escaping you are opting out of. So the moment the source HTML is attacker-influenced, you have reopened the injection surface that React normally closes for you.
The confusion with the older react-html-parser package makes this worse. react-html-parser is effectively unmaintained and has documented security concerns, and people migrating from it to html-react-parser sometimes assume the newer, healthier package also fixed the safety story. It did not, because there was never a safety story to fix. Neither library sanitizes.
How to Use It Safely: Sanitize First
The fix is not to abandon the library. It is to put a real sanitizer in front of it. DOMPurify is the standard choice: it is a dedicated XSS sanitizer that strips dangerous elements, attributes, and URL schemes while preserving safe markup. You sanitize the string, then parse the clean result.
import DOMPurify from "dompurify";
import parse from "html-react-parser";
function SafeHtml({ dirty }) {
const clean = DOMPurify.sanitize(dirty, {
USE_PROFILES: { html: true },
});
return <div className="prose">{parse(clean)}</div>;
}
Order matters: sanitize, then parse. Sanitizing after parsing is too late, because the dangerous nodes already exist as React elements. Keep DOMPurify current, since sanitizer bypasses are found and patched periodically, and pin an allowlist of tags and attributes tight enough for your actual content rather than accepting everything HTML permits.
For defense in depth, layer a Content Security Policy. A CSP that forbids inline event handlers and restricts script sources turns a sanitizer bypass from an instant compromise into a blocked attempt. On modern browsers, Trusted Types can enforce that only sanitized values ever reach dangerous sinks. Sanitizer plus CSP is the combination the security community recommends, because neither alone is sufficient.
When You Should Not Use an HTML Parser At All
If the HTML you are rendering is fully under your control, generated by your own trusted markdown pipeline with a hardened renderer, the parser is fine and you can skip the sanitizer, though I would still keep it for the same reason you wear a seatbelt on an empty road.
If the content is user-generated (comments, profile bios, rich-text fields, anything a stranger can type) treat it as hostile and never let it reach the parser unsanitized. And if you only need plain text, do not use an HTML parser at all. Render the string as a React child and let React's built-in escaping handle it. The safest HTML is the HTML you never parse.
Keeping the Dependency Honest
html-react-parser sits on a small tree of parsing dependencies, and DOMPurify has its own release cadence tied to disclosed bypasses. This is exactly the kind of frontend supply chain where a stale transitive dependency quietly becomes the weak link. Keeping these current is not busywork; a known DOMPurify bypass in an old version undoes the entire mitigation.
An SCA tool such as Safeguard can flag when your sanitizer or parser tree carries a known advisory transitively, which is often where the real exposure hides rather than in your direct dependencies. If you want the fuller picture on how injection defenses fit together across a frontend, our security academy works through the XSS family end to end.
FAQ
Does html-react-parser prevent XSS?
No. It parses HTML into React elements but does not sanitize, so untrusted input can still carry XSS. Pair it with a dedicated sanitizer such as DOMPurify, running the sanitizer first.
What is the difference between html-react-parser and react-html-parser?
html-react-parser is actively maintained and widely used, while react-html-parser is effectively unmaintained with known concerns. Neither sanitizes input, so migrating between them does not change your XSS exposure.
Should I sanitize before or after parsing?
Before. Sanitize the HTML string with DOMPurify, then pass the cleaned string to the parser. Sanitizing after parsing is too late because the unsafe nodes already exist as React elements.
Is it safe to render trusted HTML with html-react-parser without DOMPurify?
If the HTML is entirely generated by your own trusted, hardened pipeline and no user input reaches it, the risk is low. For any user-influenced content, always sanitize first and add a Content Security Policy as a second layer.