Hydration Proof

Search documentation

Find a page or section

HP1007: Different element rendered on server and client

HP1007 (element mismatch): the server rendered one element and the client another in the same place, so React threw the server HTML away. Causes and fixes.

HP1007 (element-mismatch) means the server rendered one element and React rendered a different one in the same place, such as a <ul> on the server and a <select> in the browser. React cannot reuse the server's element, so it discards that part of the page and renders it again. Render the same element type on both sides.

CodeHP1007
Nameelement-mismatch
Default severityError
GroupDOM mismatches
What it meansThe server rendered one element and the client rendered a different one in the same place.

What HP1007 (element-mismatch) means

hydration-proof compares the DOM right before and inside the hydration commit, and every node keeps its identity, so it sees exactly which element React replaced and with what. The finding shows the two tags, for example server: "<ul>" and client: "<select>", on the element's position in the page.

A tag change is structural: React throws the server HTML away up to the nearest Suspense boundary, which is slower and resets state in that part of the page. When React re-rendered a branch around the element, the finding's evidence names that branch.

When invalid HTML is the reason the elements differ (the browser moved a <div> out of a <p>), the difference is reported as HP3001 instead, with this one folded into it.

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 the server rendered HTML didn't match the client and Expected server HTML to contain a matching.

Likely causes

  • A layout chosen with matchMedia or the window size during render: a media query.
  • A branch on typeof window, the user agent or another browser-only API, such as a link on one side and a <span> on the other.
  • A flag from browser storage or data the client fetched again: different data.

How to fix it

  • Render the same element type on server and client; branch on data that both sides share.

A menu that picks its element from the screen size:

nav-menu.tsx
"use client";
 
export function NavMenu() {
  const narrow =
    typeof window !== "undefined" &&
    window.matchMedia("(max-width: 600px)").matches;
 
  return narrow ? (
    <select aria-label="Menu">
      <option>Docs</option>
    </select>
  ) : (
    <ul>
      <li>Docs</li>
    </ul>
  );
}

Render both and let CSS show one, so the markup is the same everywhere:

nav-menu.tsx
export function NavMenu() {
  return (
    <>
      <select className="nav-narrow" aria-label="Menu">
        <option>Docs</option>
      </select>
      <ul className="nav-wide">
        <li>Docs</li>
      </ul>
    </>
  );
}

When the choice really needs JavaScript, render the server's version first and switch after hydration, or use useSyncExternalStore with a getServerSnapshot that returns the server's value.

Prevent it with ESLint

no-match-media-in-render, no-window-render-branch and no-client-only-initial-state report the browser reads that pick a different element.

Example

  ✖ /docs 1.3s  1 error
    HP1007 Different element rendered on server and client  (screen size or media query read during render, 84%)
      #nav > select  in NavMenu
      server: "<ul>"
      client: "<select>"
      components/nav-menu.tsx:9:5
      → Use CSS media queries for layout differences, or read matchMedia / window size after mount.