dangerouslySetInnerHTML

Replace an element's contents with a raw, trusted HTML string instead of React children.

Signature
<div dangerouslySetInnerHTML={{ __html: '<p>some html</p>' }} />

Parameters

ParameterTypeDescription
$dangerouslySetInnerHTMLoptional{ __html: string }An object with a single __html key holding the raw HTML string. The awkward wrapper exists so the call site cannot be written by accident.

Errors

React throws if the same element is given both children and dangerouslySetInnerHTML.

This prop overrides the DOM node's innerHTML. React normally escapes every string you render, which is what makes JSX safe by default; dangerouslySetInnerHTML opts out of that for one element.

When to reach for it

  • HTML produced by your own pipeline — a Markdown renderer, a CMS field, a syntax highlighter.
  • Inline SVG or a snippet a third party hands you as a string.

The rule the name is warning about

React does not sanitize the string. Anything inside it becomes live markup, so if any part of it can be influenced by a user you have a cross-site scripting hole. innerHTML will not execute a <script> tag, which lulls people into thinking the string is harmless — but event attributes do fire, so <img src=x onerror=...> runs immediately.

Sanitize on the server if you can, and with a maintained library such as DOMPurify if you cannot. A hand-rolled regex that strips <script> is not a sanitizer.

What trips people up

React does not manage anything inside the injected markup: no synthetic events, no reconciliation, no refs. It replaces the subtree wholesale whenever the string changes, so any focus or scroll state inside it is lost. If the content needs interactivity, parse it into components instead.

Example

Try it yourself
Loading editor…
Loading React…

When to use it

  • Rendering HTML compiled from Markdown in a docs page
  • Displaying a rich-text field authored in a CMS by trusted staff
  • Injecting an SVG sprite or icon string fetched at build time
  • Showing server-highlighted code with its syntax spans intact

Frequently asked questions

How do I render an HTML string in React?

Pass it as dangerouslySetInnerHTML={{ __html: yourString }} on a wrapper element, and only after the string has been sanitized. There is no safe built-in alternative — rendering the string as a child deliberately escapes it into visible text.

Is dangerouslySetInnerHTML safe if the HTML comes from my own database?

Only if nothing user-supplied can reach that column. Comments, profile fields, and imported feeds all count as untrusted no matter which side of your stack they are stored on, so sanitize them before they are written.

Why do I get 'Can only set one of children or dangerouslySetInnerHTML'?

Because the element has both nested JSX and the prop, and React cannot own the contents twice. Move the prop onto its own empty element.

More examples

Sanitizing before injecting

DOMPurify strips the onerror attribute and leaves the paragraph, so the rendered output is just "Great post!" plus a neutered img. The sanitize call is memoized on the input string because it walks the whole document fragment; running it on every render of a long thread is measurable. Sanitizing on write, before the value is stored, is better still — then the read path cannot be forgotten.

Example · jsx
import { useMemo } from "react";
import DOMPurify from "dompurify";

function RichText({ html }) {
  const clean = useMemo(
    () => DOMPurify.sanitize(html, { USE_PROFILES: { html: true } }),
    [html]
  );
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

const COMMENT = '<p>Great post!</p><img src="x" onerror="steal()">';

export default function CommentThread() {
  return (
    <article>
      <h4>Comment</h4>
      <RichText html={COMMENT} />
    </article>
  );
}

Why React re-creates the subtree

Type into the injected input, then toggle. Whatever you typed is gone, and so is focus: React does not diff inside the injected markup, it assigns innerHTML again and the browser discards the old nodes. This is the practical reason to keep interactive content out of an HTML string and render it as components.

Example · jsx
import { useState } from "react";

export default function Preview() {
  const [bold, setBold] = useState(false);
  const html = bold
    ? "<p><strong>Draft copy</strong></p><input placeholder='type here' />"
    : "<p>Draft copy</p><input placeholder='type here' />";

  return (
    <div>
      <button onClick={() => setBold(!bold)}>Toggle bold</button>
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </div>
  );
}

Discussion

  • Be the first to comment on this lesson.