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

NextJS App Router迁移后Material-UI useColorScheme报错求助

解决NextJS App Router中Material-UI useColorScheme 必须在<CssVarsProvider />下调用的报错

问题描述

迁移到NextJS 13 App Router后,触发Material-UI报错:

Error: MUI: useColorScheme must be called under

return (<CssVarsProvider />)

该报错在<CssVarsProvider />外部调用useColorScheme时触发。但已在根layout.tsx中通过自定义ThemeProvider包裹所有页面,ThemeProvider为客户端组件(含'use client'标记),内部已初始化<CssVarsProvider />,但调用useColorScheme的UsesColorSchemeHook组件仍触发报错。

相关代码如下:
根layout.tsx

export default async function Layout() {
    return (
        <html>
        <body>
        <ThemeProvider>
            <UsesColorSchemeHook />
        </ThemeProvider>
        </body>
        </html>
    );
}

自定义ThemeProvider

'use client';
// ...
export default function ThemeProvider({ children }) {
    // ...

    return (
        <CacheProvider value={cache}>
            <CssVarsProvider theme={theme} modeStorageKey="color_mode">
                <CssBaseline enableColorScheme />
                {children}
            </CssVarsProvider>
        </CacheProvider>
    );
}

UsesColorSchemeHook组件

'use client';
// ...
export default function UsesColorSchemeHook({ children }) {
    const { mode } = useColorScheme();

    return <div>{mode}</div>;
};

原因分析

NextJS App Router中,根layout.tsx默认是服务器组件。当它直接渲染客户端组件UsesColorSchemeHook时,NextJS会尝试提前对客户端组件进行流式拆分渲染,此时作为客户端组件的ThemeProvider还未完成初始化,导致UsesColorSchemeHook在<CssVarsProvider />上下文未就绪的情况下调用useColorScheme,触发报错。

解决方案

1. 确保客户端组件上下文层级正确

修改根layout.tsx,让ThemeProvider作为body的直接子元素,且所有依赖上下文的组件都严格嵌套在ThemeProvider内部:

export default async function Layout() {
  return (
    <html>
      <body>
        <ThemeProvider>
          {/* 所有依赖MUI上下文的组件必须放在此处 */}
          <UsesColorSchemeHook />
        </ThemeProvider>
      </body>
    </html>
  );
}

2. 给依赖上下文的组件添加就绪检查

在UsesColorSchemeHook中手动判断上下文是否就绪,避免提前调用useColorScheme:

'use client';
import { useColorScheme, ColorSchemeContext } from '@mui/material/styles';
import { useContext } from 'react';

export default function UsesColorSchemeHook({ children }) {
  const colorSchemeContext = useContext(ColorSchemeContext);
  
  // 上下文未就绪时返回加载态或空内容
  if (!colorSchemeContext) return <div>Loading...</div>;

  const { mode } = useColorScheme();
  return <div>{mode}</div>;
};

3. 用客户端中间层统一包裹上下文

创建纯客户端组件作为上下文入口,隔离服务器组件与客户端上下文的渲染时序:
创建ClientLayout.js

'use client';
import ThemeProvider from './ThemeProvider';

export default function ClientLayout({ children }) {
  return (
    <ThemeProvider>
      {children}
    </ThemeProvider>
  );
}

修改根layout.tsx

import ClientLayout from './ClientLayout';

export default async function Layout() {
  return (
    <html>
      <body>
        <ClientLayout>
          <UsesColorSchemeHook />
        </ClientLayout>
      </body>
    </html>
  );
}

4. 排查ThemeProvider内部的异步逻辑

如果ThemeProvider内部有异步加载主题的逻辑,需确保<CssVarsProvider />在异步操作完成后再渲染:

'use client';
import { useState, useEffect } from 'react';
import { CacheProvider } from '@emotion/react';
import { CssVarsProvider, CssBaseline } from '@mui/material/styles';

export default function ThemeProvider({ children }) {
  const [theme, setTheme] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const loadTheme = async () => {
      const fetchedTheme = await import('./theme');
      setTheme(fetchedTheme.default);
      setIsLoading(false);
    };
    loadTheme();
  }, []);

  if (isLoading) return <div>Loading theme...</div>;

  return (
    <CacheProvider value={cache}>
      <CssVarsProvider theme={theme} modeStorageKey="color_mode">
        <CssBaseline enableColorScheme />
        {children}
      </CssVarsProvider>
    </CacheProvider>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 14:57:31