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.
| Code | HP1003 |
|---|---|
| Name | style-mismatch |
| Default severity | Error |
| Group | DOM mismatches |
| What it means | The 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
- A position or size computed from the window, behind a
typeof windowcheck: a browser-only API. The package's/style-mismatchtest page setstopto8pxon the server and24pxin the browser this way. - A media query or the window size read during render.
- A value restored from browser storage, such as a saved panel width.
- A random value, such as a generated color.
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:
"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:
export function Banner() {
return <div className="banner">Banner</div>;
}.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.