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

如何在Next.js的_document.js中动态为body添加自定义类?

在Next.js中动态给body添加自定义类(解决客户端导航不更新的问题)

我之前也碰到过完全一样的困扰——用Custom Document传props给body加类根本没效果,后来在Slack里了解到服务器渲染后的body在客户端导航(next/link跳转)时不会自动更新,折腾了一阵子终于找到靠谱的解决办法,分情况给你说:

情况1:只需要在首次渲染/刷新时设置类名

如果你的类名是基于服务器端的条件(比如请求头里的用户偏好、地区设置),可以在_document.js的getInitialProps里处理,这只在服务器渲染和页面刷新时生效:

// pages/_document.js
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 isDarkMode = ctx.req?.cookies?.theme === 'dark';
    return { ...initialProps, isDarkMode };
  }

  render() {
    const { isDarkMode } = this.props;
    return (
      <Html>
        <Head />
        <body className={isDarkMode ? 'dark' : ''}>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

划重点:这种方式不会在客户端跳转时更新body类,因为getInitialProps只在服务器端和页面刷新时触发。

情况2:需要客户端导航时动态更新类名(比如切换深色模式)

要解决客户端跳转后body类不更新的问题,得在_app.js里监听路由变化,手动操作DOM来修改body的类名,同时可以用localStorage持久化状态:

// pages/_app.js
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  const [isDarkMode, setIsDarkMode] = useState(false);

  // 页面加载时初始化主题
  useEffect(() => {
    const savedTheme = localStorage.getItem('theme');
    const shouldBeDark = savedTheme === 'dark';
    setIsDarkMode(shouldBeDark);
    updateBodyClass(shouldBeDark);
  }, []);

  // 监听路由切换,更新body类
  useEffect(() => {
    const handleRouteChange = () => updateBodyClass(isDarkMode);
    
    router.events.on('routeChangeComplete', handleRouteChange);
    // 组件卸载时移除监听
    return () => router.events.off('routeChangeComplete', handleRouteChange);
  }, [isDarkMode, router]);

  // 统一的body类更新方法
  const updateBodyClass = (dark) => {
    if (dark) {
      document.body.classList.add('dark');
    } else {
      document.body.classList.remove('dark');
    }
  };

  // 供子组件调用的主题切换方法
  const toggleTheme = () => {
    const newMode = !isDarkMode;
    setIsDarkMode(newMode);
    localStorage.setItem('theme', newMode ? 'dark' : 'light');
    updateBodyClass(newMode);
  };

  return (
    <Component {...pageProps} toggleTheme={toggleTheme} isDarkMode={isDarkMode} />
  );
}

export default MyApp;

如果你用的是Next.js 13+的App Router

如果已经迁移到app目录的新路由,在根布局里加客户端逻辑就能搞定:

// app/layout.js
'use client'; // 必须加这个指令才能用客户端状态和DOM操作

import { useEffect, useState } from 'react';
import { usePathname } from 'next/navigation';

export default function RootLayout({ children }) {
  const pathname = usePathname();
  const [isDarkMode, setIsDarkMode] = useState(false);

  useEffect(() => {
    const savedTheme = localStorage.getItem('theme');
    const initialDark = savedTheme === 'dark';
    setIsDarkMode(initialDark);
    updateBodyClass(initialDark);
  }, []);

  // 路由变化时更新body类
  useEffect(() => {
    updateBodyClass(isDarkMode);
  }, [pathname, isDarkMode]);

  const updateBodyClass = (dark) => {
    dark ? document.body.classList.add('dark') : document.body.classList.remove('dark');
  };

  const toggleTheme = () => {
    const newMode = !isDarkMode;
    setIsDarkMode(newMode);
    localStorage.setItem('theme', newMode ? 'dark' : 'light');
    updateBodyClass(newMode);
  };

  return (
    <html lang="en">
      <body>
        {children}
        {/* 可以加个切换按钮测试 */}
        <button onClick={toggleTheme}>
          {isDarkMode ? '切换浅色模式' : '切换深色模式'}
        </button>
      </body>
    </html>
  );
}

最后总结一下

  • 静态类名/仅服务器端判断的场景,用_document.js的getInitialProps就够了;
  • 需要动态切换或者客户端导航时更新的,必须用_app.js(Pages Router)或客户端根布局(App Router),手动操作DOM+监听路由变化;
  • 用localStorage或者用户数据库存主题状态,避免刷新页面后丢失设置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:37:00