HP1009 (missing-element) means React's first client render produces an
element or text node that the server HTML does not contain. React cannot
hydrate a node the server never sent, so it discards that part of the page and
renders it again. Render client-only nodes after mount, or with a client-only
component.
| Code | HP1009 |
|---|---|
| Name | missing-element |
| Default severity | Error |
| Group | DOM mismatches |
| What it means | The client render produces a node the server HTML does not contain. |
What HP1009 (missing-element) means
A component rendered more in the browser than on the server: a badge, a banner,
a widget that depends on something only the browser knows. The finding points
at the new node and shows it as the client value, with (absent) as the server
value.
Scripts are not counted, and neither are <link>, <meta>, <style>,
<title> and <base> tags added to <head> (HP1014
covers the head). A whitespace-only text node is
HP1015, and an element a script or extension inserted
before hydration is reported as a change outside React
(HP4001).
The React error it matches
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client.
Warning: Expected server HTML to contain a matching <div> in <div>The first is React 19, the second the React 18 development warning. See Expected server HTML to contain a matching.
Likely causes
- A count or flag read from browser storage during render.
- A
typeof window !== "undefined"branch or another browser-only API. - A section shown only on small screens: a media query.
- A list that is longer on the client because the client fetched it again: different data.
How to fix it
- Render client-only nodes after mount (
useEffect) or with a client-only component.
A cart badge that only exists in the browser:
"use client";
export function CartBadge() {
const count =
typeof window === "undefined"
? 0
: Number(localStorage.getItem("cart-count") ?? 0);
return (
<a href="/cart" id="cart">
Cart
{count > 0 && <b className="badge">{count}</b>}
</a>
);
}Read the count after hydration, so the first client render matches the server:
"use client";
import { useEffect, useState } from "react";
export function CartBadge() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(Number(localStorage.getItem("cart-count") ?? 0));
}, []);
return (
<a href="/cart" id="cart">
Cart
{count > 0 && <b className="badge">{count}</b>}
</a>
);
}For a whole component that cannot render on the server, see
client-only components and
next/dynamic with ssr: false.
Prevent it with ESLint
no-storage-in-initial-render,
no-window-render-branch and
no-match-media-in-render report the
reads that make a component render more in the browser.
Example
✖ /shop 1.2s 1 error
HP1009 Element missing from the server HTML (localstorage / sessionstorage read during render, 85%)
#cart > b in CartBadge
server: (absent)
client: "<b class=\"badge\">2</b>"
components/cart-badge.tsx:12:21
→ Storage is only available in the browser. Render a neutral value first and read storage in useEffect.