Hydration Proof

Search documentation

Find a page or section

HP1003: Inline style differs between server and client

HP1003 (style mismatch): the style attribute in the server HTML differs from the inline style React applies on the client, and React keeps the server's.

HP1003 (style-mismatch) means the style attribute in the server HTML differs from the inline style React renders on the client. React does not patch it during hydration, so the element keeps the server's position, size or color. Make inline styles depend only on data both sides have, or apply them after mount.

CodeHP1003
Namestyle-mismatch
Default severityError
GroupDOM mismatches
What it meansThe style attribute rendered on the server differs from the style React applies on the client.

What HP1003 (style-mismatch) means

The element exists on both sides, but its style prop produced different declarations. Styles are compared as parsed declarations, so formatting alone never counts: only a property whose value differs, or that exists on one side only.

React 19 reports nothing about it in production, and the page keeps the server style until the element re-renders. hydration-proof compares the style React renders for every reused element with the one in the server HTML, so it finds the difference in production builds too.

Styles written after hydration are not mismatches. A useLayoutEffect that sets element.style.top changes the element during the hydration commit, and hydration-proof treats that as an update, not as HP1003.

The React error it matches

Only development builds report it:

A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up.
Warning: Prop `style` did not match.

See attributes didn't match for the React 19 message.

Likely causes

How to fix it

  • Make inline styles depend only on data available on both server and client, or apply them after mount.

A layout decided from the window size differs as soon as the browser is not the size the server assumed:

banner.tsx
"use client";
 
export function Banner() {
  const top = typeof window !== "undefined" && window.innerWidth > 600 ? 24 : 8;
 
  return <div style={{ position: "absolute", top }}>Banner</div>;
}

Let CSS decide instead. Both renders produce the same markup, and the browser applies the right value:

banner.tsx
export function Banner() {
  return <div className="banner">Banner</div>;
}
banner.css
.banner {
  position: absolute;
  top: 8px;
}
 
@media (min-width: 601px) {
  .banner {
    top: 24px;
  }
}

When the value has to come from JavaScript (a measured height, a drag position), render a neutral style first and set the real one in an effect.

Prevent it with ESLint

no-match-media-in-render, no-browser-global-in-render and no-window-render-branch report the reads that make a style differ.

When the difference is intentional

suppressHydrationWarning on the element keeps the server style without a warning, and hydration-proof lists the difference as HP6001 (info). To skip an element entirely, add data-hydration-proof-ignore or a selector in ignore.selectors; see ignoring findings.

Example

  ✖ /home 1.0s  1 error
    HP1003 Inline style differs between server and client  (screen size or media query read during render, 84%)
      #banner  in Banner
      attribute: style
      server: "position: absolute; top: 8px;"
      client: "position: absolute; top: 24px;"
      components/banner.tsx:6:10
      → Use CSS media queries for layout differences, or read matchMedia / window size after mount.