How to safely use dangerouslySetInnerHTML in React
Published: 2024-01-08 18:48:31

As the name suggests `dangerouslySetInnerHTML` should be used cautiously. It is like the `innerHTML` property that is exposed by the DOM node.

With `dangerouslySetInnerHTML` you can set the HTML of the element. React does not perform any sanitization on the HTML set using `dangerouslySetInnerHTML`

It is called `dangerouslySetInnerHTML` because it is dangerous if the HTML that is set is unfiltered or unsanitized because it exposes the risk of injecting malicious code, XSS attack and other security threats that could compromise the application.

Hence `dangerouslySetInnerHTML` should be avoided unless absolutely necessary and before `dangerouslySetInnerHTML`, the HTML input should be sanitized.

In this blog post, we will look at some examples of how to use `dangerouslySetInnerHTML` and how to safely sanitize the HTML before setting using `dangerouslySetInnerHTML`.

## Basic Usage Example
Here is the basic usage example of `dangerouslySetInnerHTML`

```js
import React from "react";

export default function App() {
  const htmlContent = "<p>This is raw <strong>HTML</strong> content.<p>";

  return (
    <div className="App">
      <h1>Raw HTML</h1>
      <div dangerouslySetInnerHTML={{ __html: htmlContent }}></div>
    </div>
  );
}
```

In the above example, we set the raw html string stored in the variable `htmlContent`

The `htmlContent` will be set as `innerHTML` of the `<div></div>` tag. We pass `dangerouslySetInnerHTML` prop an object with the key `__html` and the value should contain the HTML string that we want to set.

The HTML should be sanitized before being used with `dangerouslySetInnerHTML` as it exposes security risks. In the above example the HTML is not sanitization and doing this not recommend as it is a bad practice and would result in sever security risks.

In the next section, we will see how to sanitize the HTML string before setting it as a value using `dangerouslySetInnerHTML`.

![Output of our application showing Raw HTML rendered](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4ihfbznxmah5ledyw4x.png)

## Sanitizing with DOMPurify

In the previous section, we specified the HTML string and setting it directly to `dangerouslySetInnerHTML` which is not a good practice.

In this section, we will use the package **DOMPurify** to santize our HTML string before using it in  `dangerouslySetInnerHTML`.

Let's first install the DOMPurify package using npm install

```bash
npm install dompurify
```
Then we will update our component to use DOMPurify:

```js
import React from "react";
import DOMPurify from "dompurify";

export default function App() {
  const htmlContent = "<p>This is raw <strong>HTML</strong> content.<p>";
  const sanitizedHtmlContent = DOMPurify.sanitize(htmlContent);
  return (
    <div className="App">
      <h1>Raw HTML</h1>
      <div dangerouslySetInnerHTML={{ __html: sanitizedHtmlContent }}></div>
    </div>
  );
}
```
Using the DOMPurify library is very easy, we just need to call the `santize` method on the DOMPurify library and it returns the sanitized version of the HTML.

We can then pass the sanitized version to dangerouslySetInnerHTML prop.

### Verifying HTML Sanitization

To check if our DOMPurify, we will inject an XSS payload into our HTML string and see if our DOMPurify correctly escapes the HTML script.

```js
import React from "react";
import DOMPurify from "dompurify";

export default function App() {
  const htmlContent = "<script>alert(1);</script>";
  const sanitizedHtmlContent = DOMPurify.sanitize(htmlContent);
  return (
    <div className="App">
      <h1>Raw HTML</h1>
      <div dangerouslySetInnerHTML={{ __html: sanitizedHtmlContent }}></div>
    </div>
  );
}
```

We have updated our `htmlContent` to `<script>alert(1);</script>` and if our HTML is not sanitized correctly then the page will display an alert with 1.

Apart from DOMPurify there are other sanitization libraries available, that you can use like sanitize-HTML. But when choosing a library make sure you use a library that is actively developed, has a large user base and is widely used.

## Alternatives to Consider before using dangerouslySetInnerHTML

dangerouslySetInnerHTML should be used only when it is absolutely necessary and should be avoided whenever possible due to the security risks.

Always other options should be considered before using dangerouslySetInnerHTML, and here are some of the options that you should consider, but make sure to santize your HTML using a library like DOMPurify first:

1. **Try to use JSX First:** You should first try to use JSX, if you have legacy code that you want to integrate, or you are integrating some 3rd party library try to use it JSX and with refs and only use dangerouslySetInnerHTML as the last resort.

2. **Use Library that converts HTML to JSX:** There are multiple libraries available which parses HTML into JSX, you can try to use those libraries as well, some of the popular options include:

- html-react-parser: It allows you to parse raw HTML and convert it into React elements. It is a safer alternative to dangerouslySetInnerHTML. However you still need to sanitize the HTML. As of writing this library has 1.6K stars on Github and 990,820 weekly downloads on npm
- react-html-parser: This also allows you to convert raw HTML into react components, and it is similar to html-react-parser. It has 742 starts on github as of writing and 277k weekly download on NPM. Also thing to note that it was last updated in 2020.

Before using these libraries make sure to sanitize the HTML using HTML sanitization libraries like DOMPurify.

## Scenarios where dangerouslySetInnerHTML could be used

Sometimes it is inevitable to use dangerouslSetInnerHTML and there are cases where you cannot get away without using dangerouslySetInnerHTML, let. discuss some of those scenarios:

1. **HTML data coming from a Trusted Source:** When your HTML content is coming from a Trusted Source like your Content Management System or from the Server Generated content. In these cases, you can use the dangerouslSetInnerHTML. But make sure that you trust the source of the data.
2. **Properly Sanitized Content:** You can safely use dangerouslySetInnerHTML content that is properly sanitized. Make sure you use a robust and well-tested sanitization library to escape any unsafe tags and XSS code.
3. **When Integrating 3rd Party Libraries:** Some 3rd Party libraries do not integrate well in React, in those cases you have to use dangerouslySetInnerHTML to integrate the library with your code. But before doing that make sure you trust the library and is well-vetted and does not expose your application to any security risk, and generate content is well-sanitized.

## Building a Markdown Editor in React using ShowDown, DOMPurify and dangerouslySetInnerHTML

Let's build a Markdown editor, that displays that Markdown output in HTML in real-time.

To build this application we will build a component that would take markdown and convert it into HTML.

We will also use the library will work in the following manner:

1. Build a React Component Accept Markdown text as a prop
2. Use ShowDown to convert Markdown to HTML
3. Use DOMPurify to sanitize the rendered HTML

## Building MarkDownViewer

We will create a `MarkdownViewer.js` component, which will accept markdown as a prop and convert it into HTML and display it on the screen.

Create a file called as `src/MarkdownViewer.js` and add the following code:

```js
import showdown from "showdown";
import DOMPurify from "dompurify";
import React from "react";

function MarkdownViewer({ md, styles, className }) {
  const converter = new showdown.Converter();
  const html = converter.makeHtml(md);
  const sanitizedHTML = DOMPurify.sanitize(html);

  return (
    <div
      styles={styles}
      className={className}
      dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
    ></div>
  );
}

export default MarkdownViewer;
```

In the above code, we have created a `MarkdownViewer` component, we have first imported the dependencies `showdown` and `dompurify`.

You can install them using npm

```bash
npm install showdown
npm install dompurify
```

Then we are creating a `converter` object and converting markdown to html.

```js
  const html = converter.makeHtml(md);
```
Then we are sanitizing the generated HTML using the `DOMPurify` library:

```js
const sanitizedHTML = DOMPurify.sanitize(html);
```
Finally, we set the generated HTML as `dangerouslySetInnerHTML` to the div tag

```js
  return (
    <div
      styles={styles}
      className={className}
      dangerouslySetInnerHTML={{ __html: sanitizedHTML }}
    ></div>
  );
```

## Building the Editor

The Editor Component is very simple, it will contain a textarea and will accept `onChange` method as a prop.

Create a file called as `src/Editor.js` to hold our Editor Component.

We will call the onChange method from the prop when the value of the textarea changes.

```js
export default function Editor({ onChange, styles, className }) {
  return (
    <textarea
      styles={styles}
      className={className}
      onChange={onChange}
    ></textarea>
  );
}
```

## Putting it all together

Now, let's open our `src/App.js` file and import the MarkdownViewer and Editor components.

We will attach the onChange listener to the Editor component, get the value from the Editor and set it as a prop to the MarkdownViewer component to display the Markdown typed by the user.

```js
import React, { useState } from "react";
import Editor from "./Editor";
import MarkDownViewer from "./MarkdownViewer";
import "./styles.css";

export default function App() {
  const [editorValue, setEditorValue] = useState("");
  function handleOnChange(event) {
    setEditorValue(event.target.value);
  }

  return (
    <div className={"container"}>
      <Editor className={"half"} onChange={handleOnChange} />
      <MarkDownViewer className={"half"} md={editorValue} />
    </div>
  );
}
```

In the above cover, we have created a state variable called as editorValue and created a method called as handleOnChange.

We are passing the handleOnChange method as a prop to the Editor component, when the value changes in the Editor Component, we are updating the editorValue when textarea changes.

Then we are passing editorValue to the Markdown viewer component.

## Demo
Here is the Demo of our Markdown Editor

## [Metered TURN servers](https://www.metered.ca/stun-turn)

1. Global Geo-Location targeting: Automatically directs traffic to the nearest servers, for lowest possible latency and highest quality performance. less than 50 ms latency anywhere around the world
2. Servers in 12 Regions of the world: Toronto, Miami, San Francisco, Amsterdam, London, Frankfurt, Bangalore, Singapore,Sydney (Coming Soon: South Korea, Japan and Oman)
3. Low Latency: less than 50 ms latency, anywhere across the world.
4. Cost-Effective: pay-as-you-go pricing with bandwidth and volume discounts available.
5. Easy Administration: Get usage logs, emails when accounts reach threshold limits, billing records and email and phone support.
6. Standards Compliant: Conforms to RFCs 5389, 5769, 5780, 5766, 6062, 6156, 5245, 5768, 6336, 6544, 5928 over UDP, TCP, TLS, and DTLS.
7. Multi‑Tenancy: Create multiple credentials and separate the usage by customer, or different apps. Get Usage logs, billing records and threshold alerts.
8. Enterprise Reliability: 99.999% Uptime with SLA.
9. Enterprise Scale: With no limit on concurrent traffic or total traffic. Metered TURN Servers provide Enterprise Scalability
10. 50 GB/mo Free: Get 50 GB every month free TURN server usage with the Free Plan
11. Runs on port 80 and 443
12. Support TURNS + SSL to allow connections through deep packet inspection firewalls.
13. Support STUN
14. Supports both TCP and UDP

## Conclusion
In this blog post, we have learned what `dangerouslySetInnerHTML` is, and how to safely use it in our application.

We have also looked at ways to avoid using dangerouslySetInnerHTML and its alternatives and also looked at ways to safely escape the HTML code before rendering it using `dangerouslySetInnerHTML`.