首页
/ Material UI 与 Next.js App Router 的 TypeScript 示例工程全解析:从项目结构到主题定制

Material UI 与 Next.js App Router 的 TypeScript 示例工程全解析:从项目结构到主题定制

2026-09-07 15:21:18作者:钟日瑜

Material UI 官方在仓库 examples/material-ui-nextjs-ts 中维护了一个面向生产实践的 TypeScript 示例工程,它演示了在 Next.js(App Router)下集成 Material UI 的完整做法:服务端渲染下的 Emotion 样式缓存、基于 CSS 变量的主题与明暗模式切换、以及如何在 TSX 中组合布局组件与页面路由。读完本文,你将掌握一套可以直接复刻到真实项目中的「Next.js + Material UI + TypeScript」初始化模板,并理解每一处关键配置背后的源码原理。

一、示例工程概述与适用场景

该示例是一个由 create-next-app 引导、随后安装并接入 Material UI 的完整 Next.js 项目,采用 App Router 目录约定与 TypeScript 类型系统。从 package.json 可以看到其依赖选型:

  • next^16.0.7
  • react / react-dom^19.0.0
  • @mui/material@mui/icons-material:最新版;
  • @mui/material-nextjs:官方为 Next.js 提供的集成包(App Router 缓存与样式注入);
  • @emotion/cache@emotion/react@emotion/styled:Material UI 默认样式引擎所需;
  • 开发依赖包含 typescript@types/react 等与 ESLint 配置。

工程目录非常精简,核心源码集中在 src/ 之下:

examples/material-ui-nextjs-ts/
├── src/
│   ├── app/                    # App Router 路由
│   │   ├── about/page.tsx      # /about 页面
│   │   ├── layout.tsx          # 根布局:主题 + 样式缓存
│   │   └── page.tsx            # / 首页
│   ├── components/             # 示例组件
│   │   ├── Copyright.tsx
│   │   ├── Link.tsx            # 桥接 MUI Link 与 Next Link
│   │   ├── ModeSwitch.tsx      # 明暗模式切换
│   │   └── ProTip.tsx
│   └── theme.ts                # createTheme 主题定义
├── next.config.mjs
└── tsconfig.json

这一结构是 Material UI 官方推荐的起点形态:根布局只做"基础设施"(缓存、主题、基线样式),页面与组件各自独立,主题集中管理,便于后续横向扩展。

二、快速启动:安装、运行与在线体验

按官方文档说明,可以直接从仓库提取该目录并运行:

curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2  material-ui-master/examples/material-ui-nextjs-ts
cd material-ui-nextjs-ts

然后安装依赖并启动开发服务器:

npm install
npm run dev

用浏览器打开 http://localhost:3000 即可看到首页;package.json 中还提供了生产级脚本:npm run build 执行 next buildnpm run start 执行 next start

如果你不想在本地搭建,官方 README 同时提供了两种云端沙箱入口(StackBlitz 与 CodeSandbox),可直接基于 examples/material-ui-nextjs-ts 目录在线打开预览。此外,src/app/about/page.tsx 演示了第二个路由 /about,它与首页之间通过 Material UI 按钮互相导航,可用于验证多页面的样式渲染与客户端路由是否正常。

三、根布局:集成 Material UI 的关键一环

App Router 下所有页面都挂在 src/app/layout.tsx 中。它按顺序完成了四件事,这也是任何 Next.js 项目中接入 Material UI 的标准步骤:

import { AppRouterCacheProvider } from '@mui/material-nextjs/v16-appRouter';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
import theme from '@/theme';
import ModeSwitch from '@/components/ModeSwitch';

export default function RootLayout(props: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <InitColorSchemeScript attribute="class" />
        <AppRouterCacheProvider options={{ enableCssLayer: true }}>
          <ThemeProvider theme={theme}>
            <CssBaseline />
            <ModeSwitch />
            {props.children}
          </ThemeProvider>
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

3.1 AppRouterCacheProvider:解决 SSR 样式注入问题

AppRouterCacheProvider 来自 @mui/material-nextjs 包(对应 App Router 的入口是 v16-appRouter)。其作用是创建一个 Emotion 缓存(cache)实例,并把所有子组件的 Emotion 样式统一写入该缓存,最终在服务端随 HTML 一起输出,从而避免样式闪烁(FOUC),保证服务端渲染结果与客户端一致。

从源码看,该 provider 还接受 options 参数,其中 enableCssLayer 用于开启 CSS @layer 样式分层:

  • v13-appRouter/appRouterV13.tsx 中,options.enableCssLayer 被声明为可选布尔值,并在注入样式时通过正则 match(/^@layer\s+[^{]*$/) 等逻辑判断如何处理样式内容;
  • 开启后,组件库样式被收纳进层叠上下文(如 @layer),有助于规避第三方全局样式与组件样式之间的优先级冲突。

示例中设为 enableCssLayer: true,正是为了在真实项目中(常伴随 Tailwind、全局 CSS)让 Material UI 样式层级更可控。

3.2 ThemeProvider + CssBaseline + InitColorSchemeScript

  • ThemeProvider theme={theme}:把 src/theme.tscreateTheme 的结果注入组件树;
  • CssBaseline:提供一套统一、简洁的全局基线样式(重置 margin、统一字体渲染、设置背景色等);
  • InitColorSchemeScript:在首屏 HTML 的 <head> 之前注入一段脚本(attribute="class"),用于在浏览器解析样式前读取本地存储或系统偏好,提前在 <html> 上设置 class,避免暗色主题首次渲染时闪烁。它要求 suppressHydrationWarning 配合使用,因此示例在 <html> 上加了该属性。

四、主题文件:CSS 变量、色彩方案与字体加载

src/theme.ts 是整个示例的样式中心,声明了 'use client'(因为 next/font 的样式对象需要在客户端可用)。它演示了三个值得关注的现代 Material UI 能力:

'use client';
import { createTheme } from '@mui/material/styles';
import { Roboto } from 'next/font/google';

const roboto = Roboto({
  weight: ['300', '400', '500', '700'],
  subsets: ['latin'],
  display: 'swap',
});

const theme = createTheme({
  colorSchemes: { light: true, dark: true },
  cssVariables: {
    colorSchemeSelector: 'class',
  },
  typography: {
    fontFamily: roboto.style.fontFamily,
  },
  components: {
    MuiAlert: {
      styleOverrides: {
        root: {
          variants: [
            {
              props: { severity: 'info' },
              style: { backgroundColor: '#60a5fa' },
            },
          ],
        },
      },
    },
  },
});

export default theme;

各部分含义如下:

  1. colorSchemes: { light: true, dark: true }:启用 Material UI v6+ 的多色彩方案支持。相比旧版 palette.mode 的手动切换,这里让主题同时声明明暗两套方案,供运行时的模式切换机制消费。
  2. cssVariables.colorSchemeSelector: 'class':要求把生成的 CSS 变量以 class 选择器挂载(对应根布局中 InitColorSchemeScript attribute="class"),将当前模式反映为 <html> 上的 CSS class,再配合 CSS 变量实现全局换肤。
  3. typography.fontFamily:使用 next/font/google 加载 Roboto,并把字体样式接入主题。这样做的好处是字体由 Next.js 自托管、自动 display: swap,无需手工在 HTML 中引 <link>
  4. components.MuiAlert.styleOverrides:示例性地展示了对组件内部样式的覆写——通过 root.variantsseverity="info" 的 Alert 单独指定背景色,说明可以在主题层声明"组件级变体规则"。

五、暗色模式切换的组件级实现

ModeSwitch.tsx 是整套明暗切换的交互端:

'use client';
import { useColorScheme } from '@mui/material/styles';
// ...
export default function ModeSwitch() {
  const { mode, setMode } = useColorScheme();
  if (!mode) {
    return null; // 首次渲染时 mode 尚未确定,先不渲染以免不一致
  }
  return (
    // ...
    <Select
      value={mode}
      onChange={(event) => setMode(event.target.value as typeof mode)}
      label="Theme"
    >
      <MenuItem value="system">System</MenuItem>
      <MenuItem value="light">Light</MenuItem>
      <MenuItem value="dark">Dark</MenuItem>
    </Select>
  );
}

它通过 useColorScheme() 拿到当前 modesetMode,用 Material UI 的 Select 提供 system / light / dark 三个选项。其中 system 会跟随操作系统偏好,而切换结果最终通过 CSS class 作用于 <html>,与 layout.tsx 中的 InitColorSchemeScripttheme.ts 中的 colorSchemeSelector: 'class' 三者闭环。组件在 mode 尚未就绪时返回 null,避免首屏水合差异。

六、MUI 路由链接与 Next.js Link 的桥接模式

Next.js 要求站内跳转使用 next/link,而 Material UI 的 <Link> 组件负责渲染样式。示例在 components/Link.tsx 中给出了官方推荐的桥接写法:

'use client';
import Link from 'next/link';

export default Link;

这个文件把 next/link 原样导出。然后在页面中通过 Material UI 的 component 注入机制让 MUI Link 渲染成 Next Link:

  • 首页 page.tsx<MaterialUILink component={NextLink} href="/about" color="secondary">
  • 关于页 about/page.tsx<Button variant="contained" component={Link} href="/">

这样既保留 MUI 主题下的颜色、排版与涟漪效果,又获得 Next.js 的客户端路由预取与导航能力。此外两个页面还共享 ProTip.tsx(提示文案 + 自定义 SvgIcon)与 Copyright.tsx(页脚版权),后者演示了 Typography + MUI Link 组合典型页脚。

七、周边配置速览

  • next.config.mjs:当前为空配置对象,说明本项目无需额外 webpack/next 插件即可运行,Material UI 的样式方案完全由根布局中的 AppRouterCacheProvider 承载;
  • tsconfig.json:开启 strict,启用 jsx: "react-jsx",并配置 paths: { "@/*": ["./src/*"] }——这也是源码里 import theme from '@/theme'@/components/... 能生效的原因;include 额外收录了 .next/types/**/*.ts 以便 Next.js 生成的类型参与编译。

八、进一步学习方向

官方为这类示例给出的延伸阅读路径包括:Next.js 的 App Router 文档(说明该工程基于 create-next-app 引导,可直接参考 Next.js 官方 docs 学习其特性与 API)与 Material UI 的定制指南(从主题定制出发,了解 createTheme、组件覆写、colorSchemes 等能力的完整参数)。在本仓库中还可以继续对比:

  • 纯 JS 版本的姊妹工程 examples/material-ui-nextjs,观察引入 TypeScript 前后在目录与类型声明上的差异;
  • App Router 集成包的源码 packages/mui-material-nextjs,了解 AppRouterCacheProvider 的缓存与样式注入细节;
  • Material UI 主题与样式系统的更多示例,可参考 docs/src/modules 中围绕主题、暗色模式封装的基础设施实现。

总而言之,这个示例工程的价值在于:它把"SSR 缓存、CSS 变量主题、明暗模式、字体、路由桥接、多页面"这些最容易踩坑的集成点浓缩在一个最小但可运行的 TypeScript 项目中。把它跑通后,再结合 src/theme.ts 中的主题结构逐步扩展自己的设计体系,即可平滑起步正式的 Material UI + Next.js 应用开发。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388