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

在Next.js App Router中自定义Material UI主题遇SSR报错求助

解决Next.js + MUI SSR中createTheme报错问题

报错原因

Next.js 13+ App Router默认组件为服务器组件,而createTheme是Material UI的客户端专属API,无法在服务器组件的执行流程中调用,因此触发错误:

Error: Attempted to call createTheme() from the server but createTheme is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.

修复步骤

1. 修正并标记主题文件为客户端组件

修改theme.js,先修复语法错误(原赋值写法不符合ES模块规范),再标记为客户端组件:

'use client';
import { createTheme } from "@mui/material/styles";

export const theme = createTheme({
  typography: {
    allVariants: {
      fontFamily: "Trebuchet MS",
    },
  },
});

2. 抽离客户端专属布局组件

新建components/ClientLayout.js,将所有依赖MUI客户端API的UI逻辑(ThemeProvider、导航栏、AppBar等)移到这里,并标记为客户端组件:

'use client';
import {
  Drawer,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  List,
  AppBar,
  Toolbar,
  Typography,
} from "@mui/material";
import AssessmentIcon from "@mui/icons-material/Assessment";
import AddIcon from "@mui/icons-material/Add";
import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
import zIndex from "@mui/material/styles/zIndex";
import { theme } from "../theme";
import { ThemeProvider } from "@mui/material/styles";

const navItems = [
  { name: "Create New", icon: <AddIcon />, url: "todo" },
  { name: "View all", icon: <FormatListBulletedIcon />, url: "todo" },
  { name: "Dashboard", icon: <AssessmentIcon />, url: "todo" },
];

const getNav = () => {
  return (
    <List>
      {navItems.map((item, index) => (
        <ListItemButton className="navbutton" href={item.url} key={index}>
          <ListItemIcon>{item.icon}</ListItemIcon>
          <ListItemText>{item.name}</ListItemText>
        </ListItemButton>
      ))}
    </List>
  );
};

export default function ClientLayout({ children }) {
  return (
    <ThemeProvider theme={theme}>
      <AppBar position="relative" sx={{ zIndex: zIndex.drawer + 1 }}>
        <Toolbar>
          <Typography variant="h4">
            --client info--
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer anchor="left" variant="permanent">
        {getNav()}
      </Drawer>
      <main>{children}</main>
    </ThemeProvider>
  );
}

3. 调整根布局为服务器组件

修改layout.js,保留它作为服务器组件(负责顶层HTML结构和metadata),并导入使用刚才创建的客户端布局:

import "./globals.css";
import ClientLayout from "./components/ClientLayout";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

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

关键说明

  • RootLayout必须保持为服务器组件,因为metadata只能在服务器组件中导出,负责页面元数据的服务器端渲染。
  • 所有依赖MUI客户端API的逻辑必须放到标记了'use client'的组件中,确保这些代码仅在客户端执行,避免服务器端调用客户端函数的冲突。
  • 之前直接在RootLayout加'use client'出现奇怪错误,是因为服务器组件专属的metadata无法在客户端组件中导出,拆分组件是最合规的解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:36:02