Using innerHTML in React

OpenJavaScript 0

Last updated: November 7, 2023.

In plain JavaScript, HTML can be rendered inside an element by setting its innerHTML property.

For example, the following code sets the HTML inside the <body> of a web page:

const bodyEl = document.querySelector('body');
bodyEl.innerHTML = `<h1>Hello world</h1>`; // Rendered to body as HTML

In React, it works differently because you are not supposed to touch the DOM directly (React does this efficiently for you).

You might try one of the following methods:

const markup = `<h1>Hello World<h1>`;

function App() {
  return (
    <main>
      <div>{ markup }</div>
      <div>{ markup.innerHTML }</div>
    </main>
  )
}

export default App;

However, the first renders the string as it is (<h1>Hello World<h1>) and the second renders nothing inside the <div> because innerHTML is being called on a string, not an element.

The solution is to add the dangerouslySetInnerHTML attribute to a JSX element. It accepts an object as an argument. The markup to be rendered is set as the value of a __html property:

const markup = `<h1>Hello World<h1>`;

function App() {
  return (
    <main>
      <div dangerouslySetInnerHTML={ { __html: markup } }></div>
    </main>
  )
}

export default App;

Security considerations

dangerouslySetInnerHTML is a powerful tool but can also provide a gateway for an XSS (cross-site scripting) attack if you do not have control over what is rendered (e.g. user input):

const markup = `<img src="" onerror=alert("XSS") />`; // malicious markup

function App() {
  return (
    <main>
      {/* Rendering like this allows the onerror script to run! */}
      <div dangerouslySetInnerHTML={{ __html: markup }}></div>
    </main>
  )
}

export default App;

If you are rendering markup that could potentially be malicious, it is a good idea to pass it through a HTML sanitizer.

For this you can use the DOMPurify library, which can be installed as follows:

npm install dompurify

Now, using it in you code prevents the onerror from running:

import DOMPurify from 'dompurify'; // import dompurify after installing...

const markup = `<img src="" onerror=alert("XSS") />`;

function App() {

  return (
    <>
      {/* ...now, if markup is passed through the sanitizer, the onerror script doesn't run*/}
      <main dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(markup) }}></main>
    </>
  )
}

export default App;

If you open dev tools and inspect the <img> element, you will see that DOMPurify has removed its onerror attribute:

<img src="">