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.
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.
LogRocket: Full visibility into your 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 combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?
Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.
No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.
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 — start monitoring for free.