Next.js/React动态设置背景色时出现Prop className不匹配报错如何解决
问题原因
这个报错是Next.js 水合(Hydration)不匹配导致的:
- Next.js 服务端渲染(SSR/SSG)阶段无法访问浏览器专属的 localStorage API,服务端生成的
<style jsx>样式内容和客户端读取 localStorage 后生成的样式内容不一致 - styled-jsx 会根据样式内容生成唯一的 className,两端 className 不一致就触发了该报错。硬编码颜色时服务端和客户端样式完全一致,因此不会报错
解决方案
核心思路
保证服务端和客户端首次渲染的内容完全一致,等待客户端挂载完成后再加载 localStorage 中的动态主题配置。
完整修改代码
如果你使用 Pages 路由
import { useState, useEffect } from 'react'; function Theme() { // 新增挂载状态,服务端和客户端首次渲染时都为 false const [isMounted, setIsMounted] = useState(false); const [dark, setDark] = useState(false); // 首次渲染用默认颜色,避免两端不一致 const [themeColor, setThemeColor] = useState('white'); useEffect(() => { // 客户端挂载完成后再读取 localStorage setIsMounted(true); const savedColor = window.localStorage.getItem('color') || 'white'; setThemeColor(savedColor); setDark(savedColor === '#141526'); }, []); function toggleTheme() { setDark(prev => { const newDark = !prev; const newColor = newDark ? '#141526' : 'white'; setThemeColor(newColor); window.localStorage.setItem('color', newColor); console.log(newDark ? 'enabled' : 'disabled'); return newDark; }); } // 未挂载时渲染和服务端一致的默认样式,避免水合不匹配 if (!isMounted) { return ( <div> <style jsx global className='theme'>{` body { background: white; } `}</style> <label htmlFor='dark'>Dark theme</label> <input type='checkbox' name='dark' id='dark' disabled /> </div> ) } return ( <div> <style jsx global className='theme'>{` body { background: ${themeColor}; } `}</style> <label htmlFor='dark'>Dark theme</label> <input type='checkbox' name='dark' id='dark' checked={dark} onClick={toggleTheme} /> </div> ); } export default Theme;
如果你使用 App 路由(Next.js 13+)
在组件顶部添加 'use client' 指令即可,其余修改逻辑和 Pages 路由一致:
'use client'; // 后续代码和上面Pages路由的实现逻辑完全相同
优化建议
你也可以改用CSS变量实现主题切换,避免动态修改 <style jsx> 内容,进一步降低水合不匹配的概率:
- 在全局CSS中定义默认和暗色主题的变量
:root { --bg-color: white; } :root.dark { --bg-color: #141526; } body { background: var(--bg-color); }
- 主题切换时只需要给
document.documentElement增删dark类即可,不需要动态生成样式。
内容的提问来源于stack exchange,提问作者user15134931
相关产品推荐
相关产品推荐

