You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Next.js中Ant Design主题色动态切换:服务端渲染不生效求解

Next.js + Ant Design 动态主题SSR失效解决方案

我之前也踩过这个坑!Next.js SSR环境下Ant Design动态主题失效的核心问题,其实是服务端没法感知客户端的主题偏好,而且Ant Design的主题样式默认是在客户端注入的,导致服务端生成的HTML还是默认主题样式,要么加载完有明显的主题闪烁,要么干脆直接不生效。下面是我亲测有效的几种解决方案,你可以根据自己的场景来选:

方案1:用getServerSideProps传递主题偏好(适合需要SSR渲染对应主题的场景)

如果你的需求是让服务端直接渲染出对应用户主题的页面(比如SEO有要求,或者用户打开页面就要看到自己之前设置的主题),这个方案就很合适:

  • 首先得把用户的主题偏好存在cookie里(因为SSR只能读取cookie,localStorage是客户端专属的)
  • 在页面的getServerSideProps方法中,从请求头的cookie里解析出用户的主题设置,传递给页面组件
  • 最后在Ant Design的ConfigProvider里,根据服务端传过来的主题值生成对应样式,服务端会自动把主题样式注入到HTML中

代码示例:

// pages/_app.js
import { ConfigProvider } from 'antd';
import { getThemeConfig } from '../utils/theme';

function MyApp({ Component, pageProps, theme }) {
  // 根据服务端传递的主题获取对应的Ant Design配置
  const themeConfig = getThemeConfig(theme);
  return (
    <ConfigProvider theme={themeConfig}>
      <Component {...pageProps} />
    </ConfigProvider>
  );
}

export async function getServerSideProps(context) {
  // 从cookie中读取主题,这里假设cookie的key是"theme",默认用light主题
  const theme = context.req.cookies.theme || 'light';
  return {
    props: { theme },
  };
}

export default MyApp;

小贴士:记得在用户切换主题时同步更新cookie,这样下次刷新页面时服务端就能拿到正确的主题值啦!

方案2:延迟客户端主题切换(适合用户主动触发主题切换的场景)

如果你的主题切换是用户主动操作的,对SEO要求不高,那可以让服务端先渲染默认主题,等客户端挂载完成后再切换到用户偏好的主题,还能加个过渡避免闪烁:

  • 服务端先渲染默认主题的页面
  • 客户端挂载后,用useEffect读取localStorage里的主题偏好,更新主题状态
  • 为了避免主题切换时的闪烁,可以在全局CSS里先把body设为透明,等主题切换完成后再显示

代码示例:

// pages/_app.js
import { useState, useEffect } from 'react';
import { ConfigProvider } from 'antd';
import { getThemeConfig } from '../utils/theme';

function MyApp({ Component, pageProps }) {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // 从localStorage读取用户之前保存的主题
    const savedTheme = localStorage.getItem('theme') || 'light';
    setTheme(savedTheme);
    // 主题切换完成后显示页面内容
    document.body.style.opacity = '1';
  }, []);

  const themeConfig = getThemeConfig(theme);
  return (
    <ConfigProvider theme={themeConfig}>
      <Component {...pageProps} />
    </ConfigProvider>
  );
}

export default MyApp;

对应的全局CSS:

/* styles/globals.css */
body {
  opacity: 0;
  transition: opacity 0.2s ease;
}

方案3:App Router模式下的解决方案(Next.js 13+)

如果你用的是Next.js 13及以上的App Router,因为服务端组件不能访问客户端API,所以得把主题逻辑放到客户端组件里:

  • 先创建一个客户端组件ThemeProvider,负责管理主题状态和Ant Design的配置
  • 在根布局layout.jsx中引入这个组件,包裹所有内容
  • ThemeProvider里用useStateuseEffect读取并更新用户主题

代码示例:

// app/components/ThemeProvider.jsx
'use client'; // 标记为客户端组件
import { useState, useEffect } from 'react';
import { ConfigProvider } from 'antd';
import { getThemeConfig } from '../../utils/theme';

export default function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    const savedTheme = localStorage.getItem('theme') || 'light';
    setTheme(savedTheme);
  }, []);

  const themeConfig = getThemeConfig(theme);
  return (
    <ConfigProvider theme={themeConfig}>
      {children}
    </ConfigProvider>
  );
}
// app/layout.jsx
import ThemeProvider from './components/ThemeProvider';
import './globals.css';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

额外注意事项

  • 建议把所有主题配置统一放到utils/theme.js里,比如:
    // utils/theme.js
    export function getThemeConfig(theme) {
      if (theme === 'dark') {
        return {
          token: {
            colorPrimary: '#1890ff',
            // 其他暗色主题配置
          },
        };
      }
      // 浅色主题配置
      return {
        token: {
          colorPrimary: '#1890ff',
        },
      };
    }
    
  • 如果你用的是Ant Design默认的emotion样式方案,要确保Next.js的SSR配置正确,一般不需要额外配置,但如果用了styled-components,需要在next.config.js里添加相关配置。

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

火山引擎 最新活动