Doğacan Bilgili A software developer who is also into 3D-modeling and animation.

Using dangerouslySetInnerHTML in a React application

3 min read 893

Using DangerouslySetInnerHTML In A React Application

This article covers the reasoning behind using the dangerouslySetInnerHTML property in a React application, which is the equivalent of the innerHTML attribute in browser DOM.

What is dangerouslySetInnerHTML?

dangerouslySetInnerHTML is a property that you can use on HTML elements in a React application to programmatically set their content. Instead of using a selector to grab the HTML element, then setting its innerHTML, you can use this property directly on the element.

When dangerouslySetInnerHTML is used, React also knows that the content of that specific element is dynamic, and, for the children of that node, it simply skips the comparison against the virtual DOM to gain some extra performance.

As the name of the property suggests, it can be dangerous to use because it makes your code vulnerable to cross-site scripting (XSS) attacks. This becomes an issue especially if you are fetching data from a third-party source or rendering content submitted by users.

When to use dangerouslySetInnerHTML

A use case where you need to set the HTML content of a DOM element is when you populate a <div> element with the data coming from a rich text editor. Imagine you have a webpage where people can submit comments and you allow them to use a rich text editor. In this case, the output of that rich text editor is likely to be HTML with tags such as <p>, <b>, and <img>.

Consider the following code snippet, which would render the string without being aware of the <b> tag in it — meaning that the output would be just the string itself without any bold text, like so: lorem <b>ipsum</b>.

const App = () => {
  const data = 'lorem <b>ipsum</b>';

  return (
    <div>
      {data}
    </div>
  );
}

export default App;

But when dangerouslySetInnerHTML is used, React becomes aware of the HTML tags and renders them properly. This time, the output would be rendered correctly with bold text (i.e., lorem ipsum).

const App = () => {
  const data = 'lorem <b>ipsum</b>';

  return (
    <div
      dangerouslySetInnerHTML={{__html: data}}
    />
  );
}

export default App;

Note that it should be an object with the __html key passed to dangerouslySetInnerHTML. Other than that, the element you use the dangerouslySetInnerHTML property on should not have any children, hence the use of <div> element as a self-closing tag.

The requirement for passing an object is just another safeguard to prevent developers from using it without going through the documentation and becoming aware of the potential danger.

Sanitization when using dangerouslySetInnerHTML

The examples above pose no danger when rendered. However, there might be some cases where an HTML element executes a script.

We made a custom demo for .
No really. Click here to check it out.

Consider the following examples where a JavaScript event is attached to an HTML element. Although these are harmless examples, they are proof of concepts that show how an HTML element can be exploited to run malicious scripts.

const App = () => {
  const data = `lorem <b onmouseover="alert('mouseover');">ipsum</b>`;

  return (
    <div
      dangerouslySetInnerHTML={{__html: data}}
    />
  );
}

export default App;


const App = () => {
  const data = `lorem ipsum <img src="" onerror="alert('message');" />`;

  return (
    <div
      dangerouslySetInnerHTML={{__html: data}}
    />
  );
}

export default App;

Luckily, there are sanitization tools for HTML, which detect potentially malicious parts in HTML code and then output a clean and safe version of it. The most popular sanitizer for HTML is DOMPurify.

Let’s use its online demo to sanitize the above-mentioned HTML codes and see how it detects and filters out parts of the code that are likely to be dangerous when executed.

Original
lorem <b onmouseover="alert('mouseover');">ipsum</b>

Sanitized
lorem <b>ipsum</b>
Original
lorem ipsum <img src="" onerror="alert('message');" />

Sanitized
lorem ipsum <img src="">

It’s good practice to use a sanitizer even when we trust the source of the data. With the DOMPurify package used, one of the examples above would be as follows:

import DOMPurify from 'dompurify'

const App = () => {
  const data = `lorem <b onmouseover="alert('mouseover');">ipsum</b>`
  const sanitizedData = () => ({
    __html: DOMPurify.sanitize(data)
  })

  return (
    <div
      dangerouslySetInnerHTML={sanitizedData()}
    />
  );
}

export default App;

The sanitizedData function returns an object with the __html key, which has a value returned from the DOMPurify.sanitize function.

As expected, when we hover over the bold text, there is no alert function executed.

Note that because DOMPurify needs a DOM tree and the Node environment does not have one, you either have to use the jsdom package to create a window object and initialize DOMPurify with it, or use the isomorphic-dompurify package alone instead, which encapsulates both the DOMPurify and jsdom packages.

If you prefer the first option, you can refer to the following snippet from the documentation of DOMPurify.

const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');

const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);

const clean = DOMPurify.sanitize(dirty);

Conclusion

In conclusion, dangerouslySetInnerHTML is nothing but a replacement of innerHTML in React and should be used with care. Although the name suggests danger in its use, taking the necessary measure by using a well-developed sanitizer ensures the code is clean and does not run unexpected scripts when rendered within the React node.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Doğacan Bilgili A software developer who is also into 3D-modeling and animation.

Leave a Reply