# 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.

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

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:

```text
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](https://hydration.jscrate.dev/docs/errors/tree-hydrated-but-attributes-didnt-match)
for the React 19 message.

## Likely causes

- A position or size computed from the window, behind a `typeof window` check:
  a [browser-only API](https://hydration.jscrate.dev/docs/causes/browser-api). The package's
  `/style-mismatch` test page sets `top` to `8px` on the server and `24px` in
  the browser this way.
- A [media query or the window size](https://hydration.jscrate.dev/docs/causes/media-query) read during
  render.
- A value restored from [browser storage](https://hydration.jscrate.dev/docs/causes/storage), such as a
  saved panel width.
- A [random value](https://hydration.jscrate.dev/docs/causes/random), 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:

```tsx title="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:

```tsx title="banner.tsx"
export function Banner() {
  return <div className="banner">Banner</div>;
}
```

```css title="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`](https://hydration.jscrate.dev/docs/rules/no-match-media-in-render),
[`no-browser-global-in-render`](https://hydration.jscrate.dev/docs/rules/no-browser-global-in-render) and
[`no-window-render-branch`](https://hydration.jscrate.dev/docs/rules/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](https://hydration.jscrate.dev/docs/issues/hp6001) (info). To skip an element entirely, add
`data-hydration-proof-ignore` or a selector in `ignore.selectors`; see
[ignoring findings](https://hydration.jscrate.dev/docs/ignoring).

## Example

```text
  ✖ /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.
```

## Related

- [HP1002: attribute differs](https://hydration.jscrate.dev/docs/issues/hp1002)
- [HP1004: class name differs](https://hydration.jscrate.dev/docs/issues/hp1004)
- [Screen size and media queries during render](https://hydration.jscrate.dev/docs/causes/media-query)
- [useEffect and two-pass rendering](https://hydration.jscrate.dev/docs/guides/useeffect-two-pass-rendering)
