๐Ÿš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
๐ŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

dangerouslySetInnerHTML: Using It Safely, When You Must

Master dangerouslySetInnerHTML in React: its syntax, mandatory sanitization with DOMPurify, and the narrow safe-use exception.

โšก Total XP: 0|๐Ÿ’ป html XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

dangerouslySetInnerHTML fundamentals.

Quick Quiz //

Why is this API named 'dangerously' instead of neutrally?


๐Ÿš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
๐ŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

dangerouslySetInnerHTML is React's deliberately alarming-named escape hatch for injecting raw HTML, bypassing default XSS protection. This lesson covers its unusual syntax, why sanitization with a library like DOMPurify is almost always required, and the one genuinely safe exception.

1The Name Is a Warning, Not a Suggestion

React's team deliberately named this API dangerouslySetInnerHTML instead of a neutral name like setInnerHTML โ€” the awkward, alarming naming is intentional, forcing developers to consciously acknowledge they're bypassing React's default XSS protection every time they use it.

2The Syntax: An Object with __html

Unlike a normal prop, dangerouslySetInnerHTML requires an object with a specific __html key rather than a plain string. This unusual shape is deliberate, adding a small amount of friction that makes accidentally setting it without thinking less likely.

3You Almost Always Need Sanitization First

If an HTML string originates from anywhere outside hardcoded source code โ€” a CMS, a user submission, a third-party API โ€” it must be sanitized with a library like DOMPurify before being passed to dangerouslySetInnerHTML, stripping dangerous tags and attributes while preserving safe formatting.

4The Only Truly Safe Exception: Hardcoded, Static HTML

The one genuinely low-risk case is HTML that's a hardcoded string literal written directly in source code, since it can never contain a runtime XSS payload. The moment any variable or external value is mixed into that string, sanitization becomes mandatory again.

5Step-by-Step Breakdown

The Name Is a Warning, Not a Suggestion. React's team deliberately named this API dangerouslySetInnerHTML instead of something neutral like setInnerHTML โ€” the awkward, alarming name is intentional, forcing you to consciously acknowledge you're bypassing React's default XSS protection every single time you write it.

The Syntax: An Object with __html. Unlike a normal prop, dangerouslySetInnerHTML requires an object with a specific __html key, not a plain string. This isn't arbitrary โ€” the extra object wrapper makes it slightly harder to set accidentally, adding one more small speed bump before raw HTML gets injected.

Why does dangerouslySetInnerHTML require {{ __html: htmlString }} instead of accepting the string directly?

  • โ†’The unusual wrapper adds deliberate friction, making accidental misuse less likely
  • โ†’It's required purely for a rendering performance optimization

You Almost Always Need Sanitization First. If the HTML string comes from ANYWHERE outside your own hardcoded source code โ€” a CMS, a user submission, a third-party API โ€” it must be sanitized with a library like DOMPurify before being passed to dangerouslySetInnerHTML. Sanitizing strips dangerous tags and attributes while keeping safe formatting intact.

The Only Truly Safe Exception: Hardcoded, Static HTML. The one genuinely low-risk case is HTML that's a hardcoded string literal written directly in your own source code โ€” it can never contain a runtime XSS payload because it never changes based on external input. The moment ANY variable or external value gets concatenated into that string, sanitization becomes mandatory again.

Mastery Achieved. You now understand dangerouslySetInnerHTML: why its name is intentionally alarming, its unusual {{ __html: ... }} syntax, why sanitization with a library like DOMPurify is required for any content that isn't a fully hardcoded string, and exactly how narrow the truly safe exception is. Next, you'll learn how to build secure authentication UI.

Level Up ๐Ÿš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Sanitizers Can Also Strip Accessibility-Relevant Attributes

Some default DOMPurify configurations strip ARIA attributes by default โ€” if injected content relies on ARIA for accessibility, explicitly allow the needed attributes in the sanitizer configuration rather than losing them silently.

SEO Implications

  • 1

    Unsanitized Injected Content Risks Both XSS and Malformed Markup

    Beyond the security risk, unsanitized HTML can also produce malformed markup that confuses crawlers โ€” sanitization helps ensure injected content remains well-formed, valid HTML.

Best Practices

Sanitize as Close to the Injection Point as Possible

Rather than trusting that data was sanitized somewhere upstream (like on the server), sanitize immediately before passing content to dangerouslySetInnerHTML, so the safety guarantee doesn't depend on trusting every other part of the pipeline.

Configure the Sanitizer's Allowlist Deliberately

Explicitly configure which tags and attributes DOMPurify allows based on actual formatting needs, rather than relying purely on defaults, to keep the injected content's capability surface as narrow as genuinely necessary.

Frequent Bugs

THE BUG

A rich-text CMS field renders correctly most of the time but occasionally allows an unexpected script to execute.

THE FIX

The CMS content is being passed to dangerouslySetInnerHTML without sanitization. Add DOMPurify.sanitize() (or an equivalent) immediately before the content reaches dangerouslySetInnerHTML.

THE BUG

Passing a plain string directly to dangerouslySetInnerHTML throws a runtime error.

THE FIX

dangerouslySetInnerHTML requires an object with a __html key, not a plain string. Wrap the string as { __html: htmlString }.

Real-World Examples

Sanitizing CMS-Sourced Blog Post Content

A blog renders post content authored in a CMS as rich HTML, since the CMS's editor produces formatted markup rather than Markdown. Because that content originates from a CMS (an external source, even if internally trusted), it's run through DOMPurify.sanitize() with an explicit allowlist of formatting tags before being passed to dangerouslySetInnerHTML.

function BlogPost({ htmlContent }) {
  const clean = DOMPurify.sanitize(htmlContent, {
    ALLOWED_TAGS: ['p', 'strong', 'em', 'a', 'ul', 'li', 'h2', 'h3'],
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Passing unsanitized user or CMS-sourced HTML directly to dangerouslySetInnerHTML

import DOMPurify from 'dompurify'; const clean = DOMPurify.sanitize(rawHtml); <div dangerouslySetInnerHTML={{ __html: clean }} />

The Solution //

Sanitize the content with DOMPurify (or an equivalent library) immediately before it's passed to dangerouslySetInnerHTML, regardless of the content's source.

The Error //

Passing a plain string to dangerouslySetInnerHTML instead of the required object wrapper

// Wrong <div dangerouslySetInnerHTML={cleanHtml} /> // Correct <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />

The Solution //

Wrap the sanitized HTML string in an object with a __html key, matching the API's required shape.

Lesson Glossary

[01]dangerouslySetInnerHTML

React's escape hatch for injecting raw HTML, bypassing default automatic escaping.

Code Preview
dangerouslySetInnerHTML={{ __html: htmlString }}

[02]__html

The required object key when using dangerouslySetInnerHTML, an intentional friction point.

Code Preview
{{ __html: sanitizedHtml }}

[03]DOMPurify

A widely used library for sanitizing HTML, stripping dangerous tags and attributes before injection.

Code Preview
DOMPurify.sanitize(rawHtml)

[04]Static HTML Exception

The narrow case where a fully hardcoded, unchanging HTML string requires no sanitization.

Code Preview
{{ __html: '<strong>Fixed</strong>' }}

Continue Learning