Matthew C.
—You want to set the inner HTML of an element in a Next.js application. Some use cases for this include:
With vanilla JavaScript, you can use the innerHTML
Web API. To set the inner HTML in React, you use the dangerouslySetInnerHTML
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:
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 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:
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?
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. The library also provides a demo of how DOMPurify works, 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.
You sanitize your HTML string with DOMPurify by calling the sanitize
method with the HTML string as an argument:
"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. Note that Client Components pre-render static HTML on the server. To prevent this pre-rendering, dynamically import 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:
import dynamic from "next/dynamic"; const ClientOnlyComponent = dynamic(() => import("./ClientComponent"), { ssr: false, loading: () => <p>Loading...</p>, }); const ClientOnlyComponentWrapper = () => { return <ClientOnlyComponent />; }; export default ClientOnlyComponentWrapper;
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
library to create a window
object that you can initialize DOMPurify
with:
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
library which allows you to easily use DOMPurify
on the client and the server. It uses DOMPurify
and jsdom
as dependencies to achieve this.
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at [email protected].
If you are a California resident, see our Supplemental notice.