You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js生产环境暗黑模式刷新闪白问题求助

问题

基于Next.js + Tailwind CSS构建的网站,使用next-themes实现主题切换功能。本地开发时无异常,但生产环境下刷新或重载页面会短暂闪现浅色主题。排查发现:生产环境刷新页面时,<html>标签上的dark类与内联样式color-scheme: dark;会短暂消失;而同类使用Next.js与next-themes的网站仅dark类会被移除后重新添加,color-scheme: dark;始终保持,本地开发时本站也表现为此正常状态,推测该差异是导致闪白的原因。

相关代码如下:

_document.tsx

import { Providers } from '@/components/Provider';
import { Head, Html, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" />
        <link
          href="https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;500;600&display=swap"
          rel="stylesheet"
        />
        <link
          rel="apple-touch-icon"
          sizes="180x180"
          href="/apple_touch_icon.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="32x32"
          href="/favicon_32x32.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="16x16"
          href="/favicon_16x16.png"
        />
        <link rel="manifest" href="/site.webmanifest" />
        {/* Google Tag Manager */}
        <script
          dangerouslySetInnerHTML={{
            __html:
              `(function(w,l){` +
              `w[l] = w[l] || [];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
              `})(window,'dataLayer');`,
          }}
        />
        {/* eslint-disable-next-line @next/next/no-sync-scripts */}
        <script src="/__ENV.js" />
      </Head>
      <body className="overflow-x-hidden dark:bg-black-300">
        <Providers>
          <Main />
          <NextScript />
        </Providers>
      </body>
    </Html>
  );
}

_app.tsx

import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import dynamic from 'next/dynamic';
import { ThemeProvider } from 'next-themes';
import '../../public/common.css';
import type { NextPageWithLayout } from '@/utils/types';
import Script from 'next/script';
import { env } from 'next-runtime-env';

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page);

  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtm.js?id=${env(
          'NEXT_PUBLIC_GTM_ID',
        )}`}
      />

      <ThemeProvider attribute="class">
        {getLayout(<Component {...pageProps} />)}
      </ThemeProvider>
    </>
  );
}
解决方案

核心原因

生产环境下服务端渲染时,next-themes无法提前注入主题相关的类名和样式,导致页面初始渲染为浅色主题,待客户端JS加载完成后才切换到深色主题,从而出现闪白现象。

修复步骤

  1. 在_document.tsx中添加服务端主题预渲染逻辑
    通过getInitialProps读取用户存储在Cookie中的主题偏好,直接在<Html>标签上渲染对应的dark类和color-scheme样式,确保页面初始渲染就是正确主题。

  2. 优化ThemeProvider配置
    明确配置enableSystem和defaultTheme,保证客户端主题切换逻辑与服务端预渲染行为一致。

修改后的_document.tsx

import { Head, Html, Main, NextScript } from 'next/document';
import { getCookie } from 'cookies-next';

export default function Document({ theme }) {
  return (
    <Html 
      lang="en" 
      className={theme === 'dark' ? 'dark' : ''}
      style={theme === 'dark' ? { colorScheme: 'dark' } : {}}
    >
      <Head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" />
        <link
          href="https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;500;600&display=swap"
          rel="stylesheet"
        />
        <link
          rel="apple-touch-icon"
          sizes="180x180"
          href="/apple_touch_icon.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="32x32"
          href="/favicon_32x32.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="16x16"
          href="/favicon_16x16.png"
        />
        <link rel="manifest" href="/site.webmanifest" />
        {/* Google Tag Manager */}
        <script
          dangerouslySetInnerHTML={{
            __html:
              `(function(w,l){` +
              `w[l] = w[l] || [];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
              `})(window,'dataLayer');`,
          }}
        />
        {/* eslint-disable-next-line @next/next/no-sync-scripts */}
        <script src="/__ENV.js" />
      </Head>
      <body className="overflow-x-hidden dark:bg-black-300">
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Document.getInitialProps = async (ctx) => {
  const initialProps = await (await import('next/document')).Document.getInitialProps(ctx);
  // 读取next-themes默认存储的theme cookie
  const theme = getCookie('theme', { req: ctx.req, res: ctx.res }) || 'light';
  return { ...initialProps, theme };
};

修改后的_app.tsx

import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import { ThemeProvider } from 'next-themes';
import '../../public/common.css';
import type { NextPageWithLayout } from '@/utils/types';
import Script from 'next/script';
import { env } from 'next-runtime-env';

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page);

  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtm.js?id=${env(
          'NEXT_PUBLIC_GTM_ID',
        )}`}
      />

      <ThemeProvider 
        attribute="class"
        enableSystem={true}
        defaultTheme="light"
      >
        {getLayout(<Component {...pageProps} />)}
      </ThemeProvider>
    </>
  );
}

额外说明

  • 服务端通过getInitialProps读取用户的主题偏好Cookie,直接在<Html>标签上设置对应属性,避免客户端JS加载前的主题闪烁。
  • 移除_document.tsx中不必要的Providers包裹,ThemeProvider已在_app.tsx中负责客户端的主题切换逻辑。

内容的提问来源于stack exchange,提问作者Ashwin07

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:47:05