SSR模式下暗黑模式初始渲染闪烁及data-theme设置咨询
在Next.js SSR中解决暗黑模式首次加载不生效的问题
核心问题是SSR阶段无法直接访问浏览器的localStorage或window对象,导致首次渲染的根元素(<html>)没有带上data-theme属性。要解决这个问题,需要通过Cookie传递用户主题偏好,让服务端在渲染时就能注入正确的属性。
步骤1:在客户端存储主题偏好并同步到Cookie
用户切换主题时,除了更新localStorage和根元素的data-theme,还要把主题值写入Cookie,这样服务端能读取到:
const toggleTheme = () => { const currentTheme = document.documentElement.dataset.theme; const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; // 更新根元素属性 document.documentElement.dataset.theme = newTheme; // 存储到localStorage localStorage.setItem('theme', newTheme); // 写入Cookie,设置长期有效期 document.cookie = `theme=${newTheme}; path=/; max-age=31536000`; };
步骤2:在_document.tsx中从Cookie读取主题并注入根元素
_document.tsx是Next.js的SSR入口文件,在这里可以通过请求对象读取Cookie,然后给<html>标签设置data-theme:
import Document, { Html, Head, Main, NextScript } from 'next/document'; class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx); // 解析请求头中的Cookie,提取主题值 const themeCookie = ctx.req?.headers.cookie?.split('; ') ?.find(row => row.startsWith('theme=')) ?.split('=')[1]; // 优先级:Cookie存储的主题 > 系统主题偏好(仅客户端)> 默认light const userTheme = themeCookie || ( typeof window !== 'undefined' ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' : 'light' ); return { ...initialProps, userTheme }; } render() { return ( <Html data-theme={this.props.userTheme}> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
步骤3:在_app.tsx中同步客户端状态(可选但保险)
为了避免客户端和服务端状态不一致,在_app.tsx中再做一次同步,确保客户端渲染时主题正确:
import { useEffect } from 'react'; function MyApp({ Component, pageProps }) { useEffect(() => { const savedTheme = localStorage.getItem('theme'); const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; const targetTheme = savedTheme || systemTheme; if (document.documentElement.dataset.theme !== targetTheme) { document.documentElement.dataset.theme = targetTheme; } }, []); return <Component {...pageProps} />; } export default MyApp;
注意事项
- 全局样式必须基于
data-theme属性编写,比如:
:root[data-theme='light'] { --bg: #fff; --text: #000; } :root[data-theme='dark'] { --bg: #1a1a1a; --text: #fff; }
内容的提问来源于stack exchange,提问作者RikuS3n
相关产品推荐
相关产品推荐

