# HP1013: dangerouslySetInnerHTML differs between server and client

> HP1013 (inner HTML mismatch): the HTML injected with dangerouslySetInnerHTML differs between server and client, and React never patches it. Causes and fix.

Source: https://hydration.jscrate.dev/docs/issues/hp1013
Last updated: 2026-09-18

HP1013 (`inner-html-mismatch`) means the markup you inject with
`dangerouslySetInnerHTML` is different on the server and on the client. React
does not patch it during hydration, so the page keeps the server's version, and
React 19 production builds say nothing. Make the injected HTML
identical on both sides, or render it after mount.

| | |
| --- | --- |
| Code | `HP1013` |
| Name | `inner-html-mismatch` |
| Default severity | Error |
| Group | DOM mismatches |
| What it means | The HTML injected with dangerouslySetInnerHTML differs between the server and the client. React does not patch it. |

## What HP1013 (`inner-html-mismatch`) means

The element exists on both sides, but the `__html` string React renders on the
client does not produce the same content as the server HTML. hydration-proof
parses the client's string in an inert document and compares the result with
the element's content, so markup that parses to the same thing (`<br/>` and
`<br>`, for example) is not reported.

The finding has no single server or client value; the HTML report shows both
versions of the element side by side.

## The React error it matches

React 18 warns about it in development:

```text
Warning: Prop `dangerouslySetInnerHTML` did not match.
```

See [Prop `className` did not match](https://hydration.jscrate.dev/docs/errors/prop-classname-did-not-match),
which covers this variant of the warning too. React 19 production builds report
nothing.

## Likely causes

- HTML built behind a `typeof window` check, or processed only in the browser
  (sanitized, highlighted, rewritten): a
  [browser-only API](https://hydration.jscrate.dev/docs/causes/browser-api). The package's `/dangerous-html`
  test page injects different markup on each side this way.
- A string that contains a [time](https://hydration.jscrate.dev/docs/causes/time) or a number formatted with
  the runtime's [locale](https://hydration.jscrate.dev/docs/causes/locale).
- Content the client fetched again: [different data](https://hydration.jscrate.dev/docs/causes/data).

## How to fix it

- Make the injected HTML identical on server and client, or render it after
  mount.

Processing the HTML only in the browser gives the two renders different
strings:

```tsx title="post-body.tsx"
"use client";

import { highlight } from "./highlight";

export function PostBody({ html }: { html: string }) {
  const content = typeof window === "undefined" ? html : highlight(html);
  return <div id="post-body" dangerouslySetInnerHTML={{ __html: content }} />;
}
```

Do the processing once, where the data is loaded (on the server or at build
time), and pass the finished string to both renders:

```tsx title="post-body.tsx"
export function PostBody({ html }: { html: string }) {
  return <div id="post-body" dangerouslySetInnerHTML={{ __html: html }} />;
}
```

## Prevent it with ESLint

[`no-window-render-branch`](https://hydration.jscrate.dev/docs/rules/no-window-render-branch) reports the
server/browser branch, and
[`no-date-in-render`](https://hydration.jscrate.dev/docs/rules/no-date-in-render) and
[`no-locale-without-explicit-locale`](https://hydration.jscrate.dev/docs/rules/no-locale-without-explicit-locale)
the values that change the string.

## Example

```text
  ✖ /blog/launch 1.0s  1 error
    HP1013 dangerouslySetInnerHTML differs between server and client  (browser-only api used during render, 60%)
      #post-body  in PostBody
      The markup injected with dangerouslySetInnerHTML differs from what the client renders.
      components/post-body.tsx:7:10
      → Code like `typeof window !== "undefined"` or window/navigator/document access renders differently on the server. Read browser values in useEffect, or render the component on the client only.
```

## Related

- [HP1001: text differs](https://hydration.jscrate.dev/docs/issues/hp1001)
- [HP1002: attribute differs](https://hydration.jscrate.dev/docs/issues/hp1002)
- [Browser-only APIs during render](https://hydration.jscrate.dev/docs/causes/browser-api)
- [Why some errors only appear in production](https://hydration.jscrate.dev/docs/guides/hydration-error-only-in-production)
