How to use innerHTML in React?

The dangerouslySetInnerHTML attribute is React's replacement for using innerHTML in the browser DOM. Just like innerHTML, it is risky to use this attribute considering cross-site scripting (XSS) attacks.

React dangerouslySetInnerHTML

As you might know we should keep away of direct editing DOM elements when we use React to keep Virtual DOM and real DOM consistent. But sometimes we need to render HTML text cause it could be a part of user's content (comments, posts and etc). For that reason React has special attribute dangerouslySetInnerHTML. We can't get the data by this attribute as we could with innerHTML, and we can only pass html markup.

To apply HTML string value you just need to pass an object with __html as key and HTML text as value.

export function MyComponent() {
const htmlText = "<b>hello</b>"
return <div dangerouslySetInnerHTML={{ __html: htmlText }} />;

As output you will see: hello instead of <b>hello</b>

Why dangerouslySetInnerHTML is dangerous

Imagine your website has comments block with rich text editor that allows users to write styled text in HTML. When we can put any kind of HTML to the page it means we can put scripts and run it on all visitors browsers. It might be so risky.

HTML5 specification prevent running a <script> tag inserted with innerHTML or dangerouslySetInnerHTML, but we can pass script as listener handler:

const img = "<img src='1' onerror="alert('Error loading image')">"
const name = "<b onmouseover="alert('mouseover');">Mike</b>"

The good news is that there are HTML sanitization tools available that detect potentially malicious sections in HTML code and produce a clean and safe version of it. DOMPurify is the most popular HTML sanitizer.

To sanitize untrusted HTML text we can use DOMPurify.sanitize method and then put it to our React component:

import DOMPurify from 'dompurify'
export function MyComponent() {
const img = "<img src='1' onerror="alert('Error loading image')">"
return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(img) }} />;

February 03, 2022