# Prop className did not match

> Prop className did not match: React 18 found a different class in the server HTML, usually from styled-components, Emotion, MUI or a theme. How to fix it.

Source: https://hydration.jscrate.dev/docs/errors/prop-classname-did-not-match
Last updated: 2026-09-18

Prop className did not match is a React 18 development warning: an element's
`class` in the server HTML differs from the `className` React computed in the
browser. It usually comes from a CSS-in-JS library that generated class names
in a different order, or from a theme or screen size read during render. React
keeps the server's class.

## The error

React 18 prints the prop name and both values, then a component stack:

```text
Warning: Prop `className` did not match. Server: "sc-abc" Client: "sc-xyz"
```

The same warning exists for every prop. These are the ones you see most:

```text
Warning: Prop `className` did not match. Server: "MuiBox-root css-1rs9h4" Client: "MuiBox-root css-8kq2d1"
Warning: Prop `id` did not match. Server: "react-select-2-live-region" Client: "react-select-3-live-region"
Warning: Prop `style` did not match. Server: "color:black" Client: "color:white"
Warning: Prop `dangerouslySetInnerHTML` did not match. Server: "<b>Hi</b>" Client: "<b>Hello</b>"
```

Production builds do not check props, so there is no minified code. In React
19 the warning is replaced by a diff line in
["A tree hydrated but some attributes… didn't match"](https://hydration.jscrate.dev/docs/errors/tree-hydrated-but-attributes-didnt-match).

## What prop className did not match means

React 18 hydrates an element by comparing each prop of your first client
render with the attribute in the server HTML. When they differ, it warns and
moves on. It does not update the attribute, so the element keeps the server's
class, and the styles written for the client's class name do not apply. The
component looks unstyled or wrongly styled until it re-renders with a
different class.

React 18 warns only once per page load, for the first difference it finds.
Fixing one can reveal the next.

## Common causes

- **styled-components without server rendering set up.** Class names such as
  `sc-abc` come from a component id and a hash of the styles. Without the
  compiler plugin, the ids depend on the order components were created, which
  differs between the server and the browser. See
  [CSS-in-JS class names](https://hydration.jscrate.dev/docs/causes/css-in-js).
- **Emotion or MUI without the cache provider.** `css-` hashes are inserted in
  a different order on each side. MUI's Next.js integration,
  `@mui/material-nextjs`, provides the `AppRouterCacheProvider` for this.
- **Styled components created inside render.** Every render creates a new
  component with a new id.
- **Theme classes.** A `dark` class chosen from `localStorage` or
  `matchMedia` during render. See [theme hydration errors](https://hydration.jscrate.dev/docs/causes/theme).
- **Screen size.** A class chosen from `window.innerWidth`. See
  [media queries](https://hydration.jscrate.dev/docs/causes/media-query).
- **Ids from counters or `Math.random()`**, including libraries that count
  instances, such as react-select. See [unstable ids](https://hydration.jscrate.dev/docs/causes/unstable-id).
- **`dangerouslySetInnerHTML` built from values that differ,** such as a date
  or a user's locale.

## How to fix it

1. **Set up your CSS-in-JS library for server rendering.** For
   styled-components in Next.js, turn on the compiler support, then add the
   style registry described in [CSS-in-JS](https://hydration.jscrate.dev/docs/causes/css-in-js):

   ```ts title="next.config.ts"
   import type { NextConfig } from "next";

   const nextConfig: NextConfig = {
     compiler: {
       styledComponents: true,
     },
   };

   export default nextConfig;
   ```

2. **Define styled components once, at module level.** Before, a new
   component is created on every render:

   ```tsx title="components/badge.tsx"
   "use client";

   import styled from "styled-components";

   export function Badge({ tone }: { tone: string }) {
     const Pill = styled.span`
       color: ${tone};
     `;
     return <Pill>New</Pill>;
   }
   ```

   After, the component is created once and the value is passed as a prop:

   ```tsx title="components/badge.tsx"
   "use client";

   import styled from "styled-components";

   const Pill = styled.span<{ $tone: string }>`
     color: ${(props) => props.$tone};
   `;

   export function Badge({ tone }: { tone: string }) {
     return <Pill $tone={tone}>New</Pill>;
   }
   ```

3. **Generate ids with `useId()`.** For react-select, pass a stable
   `instanceId`:

   ```tsx title="components/country-select.tsx"
   "use client";

   import { useId } from "react";
   import Select from "react-select";

   export function CountrySelect() {
     return <Select instanceId={useId()} options={[]} />;
   }
   ```

4. **Move theme and screen size classes out of render.** Read them on the
   server (a cookie) or after hydration, or use CSS media queries.

## Find every instance

hydration-proof compares each attribute with the props React rendered and
reports class differences as [HP1004](https://hydration.jscrate.dev/docs/issues/hp1004), other attributes
as [HP1002](https://hydration.jscrate.dev/docs/issues/hp1002), inline styles as
[HP1003](https://hydration.jscrate.dev/docs/issues/hp1003) and injected HTML as
[HP1013](https://hydration.jscrate.dev/docs/issues/hp1013). For elements a library created, it points at
the component that used the library:

```bash
npx hydration-proof test
```

No linter can see CSS-in-JS class generation, but the
[ESLint plugin](https://hydration.jscrate.dev/docs/eslint) catches the other sources:
[`no-unstable-id`](https://hydration.jscrate.dev/docs/rules/no-unstable-id),
[`no-random-in-render`](https://hydration.jscrate.dev/docs/rules/no-random-in-render),
[`no-match-media-in-render`](https://hydration.jscrate.dev/docs/rules/no-match-media-in-render) and
[`no-browser-global-in-render`](https://hydration.jscrate.dev/docs/rules/no-browser-global-in-render).

## Related

- [Fix styled-components hydration errors](https://hydration.jscrate.dev/docs/causes/css-in-js)
- [useId and generated id mismatches](https://hydration.jscrate.dev/docs/causes/unstable-id)
- [The React 19 attribute warning](https://hydration.jscrate.dev/docs/errors/tree-hydrated-but-attributes-didnt-match)
- [HP1004: class name differs](https://hydration.jscrate.dev/docs/issues/hp1004)
- [Extra attributes from the server](https://hydration.jscrate.dev/docs/errors/extra-attributes-from-the-server)
