首页
/ Material UI 接入 Next.js App Router 官方示例(JavaScript 版)解读与实践

Material UI 接入 Next.js App Router 官方示例(JavaScript 版)解读与实践

2026-09-07 11:43:54作者:余洋婵Anita

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.jsonname 声明为 material-ui-nextjsprivate: 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 devbuild → next buildstart → 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.jssrc/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>
  );
}

这里形成了官方推荐的嵌套顺序,其作用依次为:

  1. 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 等工具类共存时更易管理覆盖优先级)。是否需要开启取决于你的样式冲突情况。
  2. ThemeProvider:接收来自 theme.js 的主题对象,向整棵组件树提供调色板、排版与组件级样式配置。

  3. 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:这是主题级组件定制的典型写法——对 MuiAlertroot slot 追加覆盖,并通过 variants 机制声明"当 severityinfo 时根节点背景为 #60a5fa"的条件样式。相比在组件内部使用 sx,把这类视觉规则收敛进主题,能保证"样式策略集中管理",是团队项目中推荐的维护方式。它也是原 README 所链接的"Customizing Material UI"文档主题下最直接的示例落点(本仓库的定制化相关文档内容位于 docs/data/material/customization 目录)。

让 MUI 组件与 next/link 协同:Link 桥接组件

App Router 下,站内导航应尽量走 Next.js 的客户端路由(next/link)以获得无整页刷新的体验,而 MUI 的 LinkButton 等组件默认渲染的是原生 <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(这里是 Linksecondary 色、Buttoncontained 变体)负责,底层元素却替换为 Next.js 路由链接,从而同时获得 MUI 外观与 App Router 的客户端导航能力。这一层若不显式处理,<Link> 会退化为整页刷新,<Button> 内的站内跳转也无法复用 Next 的预取与缓存。

页面组装与可复用展示组件

首页 src/app/page.js 与关于页 src/app/about/page.js 展示了用 ContainerBox(配合 sx 实现 Flex 垂直居中与 my/mb 间距)与 Typography 快速搭建页面的手法。两页均复用了同一组 components/ 组件:

  • ProTip.js:结合 TypographySvgIcon(内联灯泡图标 path)与 MUI Link 输出一条提示语,演示"图标 + 文字 + 链接"的最小组合;
  • Copyright.js:输出 © <年份> Your Website 页脚,动态取 new Date().getFullYear(),避免年份写死。

这两个组件是官方模板系列的常客,也是"把跨页面视觉元素抽为组件"的示范——后续扩展新页面时,只需复制 page.js 的结构并加入自己的业务区块即可。

从示例走向你自己的项目

npm run build && npm start 验证生产链路后,就可以在这个骨架上迭代。根据本仓库 examples/ 目录的实际情况,继续探索还有两条顺路:

  1. 升级 TypeScript:若团队偏好类型安全,仓库提供了对应的 TS 版本示例 examples/material-ui-nextjs-ts,其页面文件为 .tsx、并额外带 tsconfig.json,组件与主题结构保持一致,便于逐文件对照迁移;
  2. 对照其他路由/样式方案:仓库同时维护了 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.7react ^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 的桥接——全部给出了官方验证过的解法。将其作为新项目起点,或逐文件对照迁移进现有工程,都能显著缩短集成排错时间。

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