React SSR 中 class 名不匹配的根本原因是服务端与客户端 styled-components 的样式生成逻辑不一致,需确保 ServerStyleSheet 每次新建、及时 seal、避免非确定性值、theme 一致、构建配置统一,并禁用开发专用行为。

服务端生成的 class 名和客户端对不上,React 就会报 Prop `className` did not match ——这不是 styled-components 本身坏了,而是两端 class 名生成逻辑不一致。
ServerStyleSheet 必须每次渲染都新建实例
复用 ServerStyleSheet 实例是常见错误源头。一旦调用过 collectStyles(),该实例就进入“已消费”状态,再调用会直接抛错:Can't collect styles once you've consumed a ServerStyleSheet's styles!。
- 每次 SSR 渲染入口(比如 Next.js 的
renderToHTML或 Express 的res.send()前)必须new ServerStyleSheet() - 务必在
try/finally中调用sheet.seal(),否则内存泄漏风险高 - 不要在模块顶层定义 sheet 实例,避免被多个请求共享
动态样式里禁止出现非确定性值
只要插值函数返回的 CSS 字符串在服务端和客户端不完全相同,class 名哈希就会不同,hydration 就会失败。
- 禁用
Date.now()、Math.random()、useContext、useState等运行时不可复现的值 - 布尔 props 插值必须显式转成 CSS 值,比如
opacity: ${props => props.disabled ? 0.5 : 1},不能只写${props.disabled} - theme 对象必须在服务端和客户端完全一致(包括 key 名、嵌套结构、甚至 undefined vs null)
确保 Babel 和构建配置两端统一
开发环境可能正常,但构建后出问题,往往是因为 babel-plugin-styled-components 没启用,或服务端/客户端构建用了不同 hash salt。
立即学习“前端免费学习笔记(深入)”;
- 检查
.babelrc或babel.config.js是否启用了插件,并确认ssr: true开关已开 - Next.js 用户注意:app 目录下 SSR 默认启用,但若手动用
styled-components配合自定义 server,需显式传入fileName和displayName保证哈希稳定 - 禁用开发专用行为:比如 emotion 在
process.env.NODE_ENV === 'development'下会加额外前缀,而服务端常走 production 构建,导致 class 名不匹配
最隐蔽的问题往往藏在 theme 注入时机或 props 默认值处理上——比如服务端没传 size,插值里用了 props.size || 'md',客户端却从 context 读到 'lg',哪怕只差一个字符,哈希就全变了。


















