Material UI 接入 Next.js App Router 官方示例(JavaScript 版)解读与实践
Material UI 官方在仓库 examples/material-ui-nextjs 中维护了一个以 create-next-app 引导、内置 Material UI 依赖的 Next.js App Router 项目,用于演示在 React 19 + Next.js 最新版本下,如何以官方推荐方式组合 Emotion、主题与路由组件。本文以该示例的 README 为主体骨架,逐文件拆解它的目录结构、根布局、主题定制与链接集成方式,并结合同仓库源码说明其底层机制,帮助你快速跑通一个可用于实际开发的 Material UI + Next.js 起点工程。
示例是什么:官方给出的"开箱即用"起点
根据 示例 README 的描述,这是一个通过 create-next-app 初始化、随后安装 Material UI 的 Next.js 项目。它体现的并不是复杂的业务代码,而是一套被官方验证过的工程配置:
- 使用 Next.js App Router(目录即路由,
src/app); - 使用 JavaScript 而非 TypeScript(TypeScript 用户可参考同目录下的 TS 版本示例);
- 直接依赖
@mui/material与@mui/material-nextjs,后者用于在 App Router / RSC 场景下做样式注入的缓存协调; - 完整解决了字体加载、主题提供、全局 CSS Reset 与客户端导航四类"样板问题"。
当前仓库中的该示例 package.json 将 name 声明为 material-ui-nextjs、private: true,其依赖面(见 package.json)即"最小但完整"的官方推荐组合:
| 依赖 | 版本策略 | 作用 |
|---|---|---|
next |
^16.0.7 |
Next.js 框架(App Router) |
react / react-dom |
^19.0.0 |
React 19 运行时 |
@mui/material |
latest |
Material UI 核心组件库 |
@mui/icons-material |
latest |
Material 图标库 |
@mui/material-nextjs |
latest |
App Router 下 Emotion 缓存协调与字体辅助 |
@emotion/cache |
latest |
自定义 Emotion cache 的基础依赖 |
@emotion/react / @emotion/styled |
latest |
Material UI v5+ 的默认样式引擎 |
eslint / eslint-config-next |
latest |
Next.js 官方 Lint 链 |
从依赖即可看出:在 App Router 架构下,Material UI 与 Emotion、next/font 的配合不再是"可选项",而是需要在工程里显式编排的系统级配置。
快速开始:拉取目录、安装并启动
原 README 给出的使用方式分为两步:先取得示例代码,再安装依赖并启动开发服务器。
由于示例代码以目录形式存在于本仓库中,将其落地为独立项目最简单的方式是:复制 examples/material-ui-nextjs 整个目录到你的工作区(例如作为新项目根目录),随后执行:
npm install
npm run dev
然后在浏览器打开 http://localhost:3000 查看结果。若 3000 端口被占用,Next.js 会自动提示并切换到下一个可用端口。
开发之外的常用脚本同样已声明在 package.json:
npm run build # 执行 next build,产出生产构建
npm start # 执行 next start,以生产模式服务构建产物
对应关系为 dev → next dev、build → next build、start → next start。此外,原 README 还提示该示例可通过 StackBlitz / CodeSandbox 类云端沙箱直接打开体验(此为本仓库 README 提供的快捷入口,可依个人习惯选用)。适合不想在本地搭建环境、只想快速查看效果的前期调研场景。
目录结构逐层拆解
该示例的完整文件树如下(已确认存在于本仓库):
examples/material-ui-nextjs/
├── package.json # 依赖与 scripts
├── next.config.mjs # Next.js 配置(空对象,采用默认行为)
├── jsconfig.json # @/* 路径别名,指向 ./src/*
├── README.md # 示例说明
└── src/
├── app/ # App Router 路由目录
│ ├── layout.js # 根布局:CacheProvider + ThemeProvider + CssBaseline
│ ├── page.js # 首页(/)
│ ├── about/
│ │ └── page.js # 关于页(/about)
│ └── favicon.ico
├── components/
│ ├── Copyright.js # 页脚版权
│ ├── Link.js # 桥接 next/link 与 MUI 组件的可复用组件
│ ├── ProTip.js # 提示文案 + SvgIcon 演示
└── theme.js # MUI 主题定义('use client')
两个配置文件都非常精简:next.config.mjs 导出一个空配置对象(本示例无需特殊 webpack/experimental 配置);jsconfig.json 仅声明 "@/*": ["./src/*"] 路径别名,这也是代码中 @/theme、@/components/Link 等导入得以生效的原因。
src/app/page.js 与 src/app/about/page.js 构成两个真实可导航的路由页面,src/components/ 下则是被两个页面复用的展示型组件——整体麻雀虽小,但完整覆盖了"多页面 + 共享布局 + 共享主题"的常见结构。
根布局中的"三件套":缓存、主题与 CSS 基线
Material UI 与 App Router 集成时,最关键的代码集中在根布局 src/app/layout.js:
import * as React from 'react';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v16-appRouter';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import theme from '@/theme';
export default function RootLayout(props) {
return (
<html lang="en">
<body>
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
{props.children}
</ThemeProvider>
</AppRouterCacheProvider>
</body>
</html>
);
}
这里形成了官方推荐的嵌套顺序,其作用依次为:
-
AppRouterCacheProvider(最外层):导入自@mui/material-nextjs/v16-appRouter。该导出路径在包内由 packages/mui-material-nextjs/package.json 声明。它负责在 Server 端渲染时进行样式预提取、在客户端复水化时保持 Emotion 缓存一致,避免出现"首屏样式闪烁 / FOUC"。这正是 App Router 与 Pages Router 在 SSR 场景下的本质差异:App Router 下若无此 Provider,样式注入时机将难以与 React 的并发渲染协调。options={{ enableCssLayer: true }}:将 Material UI 生成的样式放入 CSS@layer级联层,使其特异性可被可预测地控制(例如与 Tailwind 等工具类共存时更易管理覆盖优先级)。是否需要开启取决于你的样式冲突情况。
-
ThemeProvider:接收来自 theme.js 的主题对象,向整棵组件树提供调色板、排版与组件级样式配置。 -
CssBaseline:MUI 的全局样式重置组件。注释中写得很明确:它"kickstart an elegant, consistent, and simple baseline",即在开发启动瞬间抹平不同浏览器默认样式差异,为后续组件渲染建立统一基准。
需要强调的是,theme.js 顶部声明了 'use client';——因为在 MUI 主题对象通过 Context 向下传递且可被客户端交互逻辑依赖,主题模块必须作为 Client Component 边界参与渲染。Server Component(如 layout.js)导入一个客户端模块并渲染其产物,是 App Router 中合法的模式。
主题定制:字体、CSS 变量与组件变体
theme.js 集中演示了 Material UI 主题的几个进阶能力:
'use client';
import { Roboto } from 'next/font/google';
import { createTheme } from '@mui/material/styles';
const roboto = Roboto({
weight: ['300', '400', '500', '700'],
subsets: ['latin'],
display: 'swap',
});
const theme = createTheme({
cssVariables: true,
palette: {
mode: 'light',
},
typography: {
fontFamily: roboto.style.fontFamily,
},
components: {
MuiAlert: {
styleOverrides: {
root: {
variants: [
{
props: { severity: 'info' },
style: {
backgroundColor: '#60a5fa',
},
},
],
},
},
},
},
});
export default theme;
要点逐一解读:
next/font/google加载 Roboto:Material UI 默认字体的首选加载方式。weight指定需要的字重集合(300/400/500/700),display: 'swap'让文字先用回退字体渲染、字体就绪后无缝切换,避免阻塞渲染与布局偏移;subsets: ['latin']控制字符子集体积。加载完成后通过roboto.style.fontFamily注入typography.fontFamily——这意味着你不需要在 HTML 里手动插<link>或额外下载字体文件,字体由 Next.js 自动优化并内联托管。cssVariables: true:开启 CSS 变量模式后,MUI 会把主题 token(如调色板颜色、间距、圆角等)输出为 CSS 自定义属性,便于运行时切换主题或在样式覆盖中引用变量,也为未来"零运行时/更轻量"的样式方案做铺垫。主题palette.mode: 'light'声明亮色模式基调。components.MuiAlert.styleOverrides.root.variants:这是主题级组件定制的典型写法——对MuiAlert的rootslot 追加覆盖,并通过variants机制声明"当severity为info时根节点背景为#60a5fa"的条件样式。相比在组件内部使用sx,把这类视觉规则收敛进主题,能保证"样式策略集中管理",是团队项目中推荐的维护方式。它也是原 README 所链接的"Customizing Material UI"文档主题下最直接的示例落点(本仓库的定制化相关文档内容位于 docs/data/material/customization 目录)。
让 MUI 组件与 next/link 协同:Link 桥接组件
App Router 下,站内导航应尽量走 Next.js 的客户端路由(next/link)以获得无整页刷新的体验,而 MUI 的 Link、Button 等组件默认渲染的是原生 <a> 或普通元素。示例用 src/components/Link.js 给出标准解法:
'use client';
import Link from 'next/link';
export default Link;
它本质上是对 next/link 的重新导出,并在文件头声明 'use client'。使用方式在页面中体现为将 MUI 组件当作"视觉层",把 next/link 通过 component prop 注入:
import MaterialUILink from '@mui/material/Link';
import NextLink from '@/components/Link';
<MaterialUILink href="/about" color="secondary" component={NextLink}>
Go to the about page
</MaterialUILink>
同样,about/page.js 中返回首页的按钮也采用了同一模式:
<Button variant="contained" component={NextLink} href="/">
Go to the home page
</Button>
component prop 是 Material UI 的经典扩展机制:样式与交互语义仍由 MUI(这里是 Link 的 secondary 色、Button 的 contained 变体)负责,底层元素却替换为 Next.js 路由链接,从而同时获得 MUI 外观与 App Router 的客户端导航能力。这一层若不显式处理,<Link> 会退化为整页刷新,<Button> 内的站内跳转也无法复用 Next 的预取与缓存。
页面组装与可复用展示组件
首页 src/app/page.js 与关于页 src/app/about/page.js 展示了用 Container、Box(配合 sx 实现 Flex 垂直居中与 my/mb 间距)与 Typography 快速搭建页面的手法。两页均复用了同一组 components/ 组件:
- ProTip.js:结合
Typography、SvgIcon(内联灯泡图标 path)与 MUILink输出一条提示语,演示"图标 + 文字 + 链接"的最小组合; - Copyright.js:输出
© <年份> Your Website页脚,动态取new Date().getFullYear(),避免年份写死。
这两个组件是官方模板系列的常客,也是"把跨页面视觉元素抽为组件"的示范——后续扩展新页面时,只需复制 page.js 的结构并加入自己的业务区块即可。
从示例走向你自己的项目
npm run build && npm start 验证生产链路后,就可以在这个骨架上迭代。根据本仓库 examples/ 目录的实际情况,继续探索还有两条顺路:
- 升级 TypeScript:若团队偏好类型安全,仓库提供了对应的 TS 版本示例 examples/material-ui-nextjs-ts,其页面文件为
.tsx、并额外带tsconfig.json,组件与主题结构保持一致,便于逐文件对照迁移; - 对照其他路由/样式方案:仓库同时维护了 Pages Router 版本(
examples/material-ui-nextjs-pages-router*)以及基于 Pigment CSS 的 Next.js/Vite 示例(examples/material-ui-pigment-css-*)。当你在"App Router + Emotion"与"Pigment CSS 零运行时"两种路线间做技术选型时,可直接对比这些目录中的配置差异。
需要留意的是:依赖在 package.json 中声明为 next ^16.0.7、react ^19.0.0,而 @mui/material 等以 latest 引入,实际安装版本会随发布窗口变化。若你的生产环境需要锁定版本,建议把 latest 替换为确定版本号并提交 package-lock.json / pnpm-lock.yaml,以保证构建可复现。
小结
examples/material-ui-nextjs 用不到十个源文件,把"Material UI × Next.js App Router"项目最容易被踩坑的四个问题——Emotion 缓存一致性(AppRouterCacheProvider)、主题与全局基线(ThemeProvider + CssBaseline)、自托管字体与 CSS 变量、以及 MUI 与 next/link 的桥接——全部给出了官方验证过的解法。将其作为新项目起点,或逐文件对照迁移进现有工程,都能显著缩短集成排错时间。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00