# How to Use dangerouslySetInnerHTML in Next.js

> To set the inner HTML in React, you use the dangerouslySetInnerHTML property, which uses the innerHTML property under the hood.

**URL:** https://sentry.io/answers/using-dangerouslysetinnerhtml-in-next-js/

---

## The Problem

You want to set the inner HTML of an element in a Next.js application. Some use cases for this include:

- Rendering user content from a rich text editor input field that may include HTML tags.
- Rendering HTML from a markdown parsing library.
- Creating a dark mode feature in a server-side rendered app, where the dark mode state is stored in local storage. (You can see an example of how this is done in this blog post: [The Quest for the Perfect Dark Mode](https://www.joshwcomeau.com/react/dark-mode/#updating-html-in-gatsby-8).)

With vanilla JavaScript, you can use the [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) Web API. To set the inner HTML in React, you use the [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html) property, which uses the `innerHTML` property under the hood. When you render text in React, it sanitizes it by default. It does not sanitize text rendered using `dangerouslySetInnerHTML`.

To use the `dangerouslySetInnerHTML` property, pass in an object with a `__html` key that has a corresponding string value for the HTML string. The React team made the property like this as a safeguard so that developers would look at documentation before using it. It's not just passing in an HTML string. The HTML string is parsed into HTML elements:

```javascript
const rawHTML = "<button>click me</button>";
return <div dangerouslySetInnerHTML={{ __html: rawHTML }} />;
```

The code above returns an HTML button. As the name suggests, it can be dangerous to set the inner HTML as you may add a [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) vulnerability into your app. This can happen if the HTML comes from content submitted by users or from a third-party source. A script could be added that could access sensitive information or give an attacker unauthorized access to an application. For example:
```javascript
const rawHTML = `<img src="" onerror="alert('You have been hacked!');" />`;
return <div dangerouslySetInnerHTML={{ __html: rawHTML }} />;
```

The `rawHTML` will be rendered, and you'll see an alert message on your screen that displays: "You have been hacked!". Note that the `<div>` tag is self-closing as the element that uses the `dangerouslySetInnerHTML` property should not have children.

Given the XSS risks, how do you use this property safely?

## The Solution

If the HTML string comes from user input or a third-party source, you need to sanitize the input. A popular sanitizer library for HTML is [DOMPurify](https://github.com/cure53/DOMPurify). The library also provides a [demo of how DOMPurify works](https://cure53.de/purify), where you can see how dirty HTML is cleaned by removing dangerous HTML like `<script`> tags.

When using DOMPurify with Next.js, you need to take server-side rendering into account. From Next.js version 13, components are server-rendered by default. Components are pre-rendered into HTML on the server before being sent to the client. The way you use DOMPurify with Next.js depends on whether you want to use it on the client side only or if you want to use it on the server as well.

### Client-side

You sanitize your HTML string with DOMPurify by calling the `sanitize` method with the HTML string as an argument:

```javascript
"use client";

import DOMPurify from "dompurify";

export default function ClientComponent() {
  const rawHTML = `<img src="" onerror="alert('You have been hacked!');" />`;
  return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(rawHTML) }} />;
}
```

DOMPurify requires a DOM tree to work. To use it in Next.js, you can use it in a [Client Component](https://nextjs.org/docs/app/getting-started/server-and-client-components). Note that Client Components pre-render static HTML on the server. To prevent this pre-rendering, [dynamically import](https://nextjs.org/docs/pages/guides/lazy-loading#nextdynamic) the component and set server-side rendering to false so that the import only occurs on the client.

The example code below shows how you can use a wrapper component and dynamic importing to render a client-only component:

```javascript
import dynamic from "next/dynamic";

const ClientOnlyComponent = dynamic(() => import("./ClientComponent"), {
  ssr: false,
  loading: () => <p>Loading...</p>,
});

const ClientOnlyComponentWrapper = () => {
  return <ClientOnlyComponent />;
};
export default ClientOnlyComponentWrapper;
```

### Server-side

If you try to run the `DOMPurify` `sanitize` method in a component that also runs on the server, you'll get the following error:

```
Error: dompurify__WEBPACK_IMPORTED_MODULE_1__.default.sanitize is not a function
```

This error occurs because `DOMPurify` requires a DOM tree to work. There is no DOM tree in the server-side Node environment.

To use DOMPurify on the server, you can use the [`jsdom`](https://www.npmjs.com/package/jsdom) library to create a `window` object that you can initialize `DOMPurify` with:

```javascript
import { JSDOM } from "jsdom";

const window = new JSDOM("").window;
const DOMPurifyServer = DOMPurify(window);

const rawHTML = `<img src="" onerror="alert('You have been hacked!');" />`;

return <div dangerouslySetInnerHTML={{ __html: DOMPurifyServer.sanitize(rawHTML) }} />;
```

Alternatively, you can use the [`isomorphic-dompurify`](https://www.npmjs.com/package/isomorphic-dompurify) library which allows you to easily use `DOMPurify` on the client and the server. It uses `DOMPurify` and `jsdom` as dependencies to achieve this.

---

*Source: [sentry.io/answers/using-dangerouslysetinnerhtml-in-next-js/](https://sentry.io/answers/using-dangerouslysetinnerhtml-in-next-js/)*
