跳到内容
+

Next.js App Router

了解如何在 Next.js App Router 中使用 MUI Base。

示例

在一个新的基于 App Router 的项目上从头开始?

通过此示例直接进入代码:MUI Base - Next.js App Router with Tailwind CSS in TypeScript。

Next.js 和 React Server Components

Next.js App Router 实现了 React Server Components,React 的一项即将推出的功能

为了支持 App Router,MUI Base 中需要访问浏览器 API 的组件和 Hook 通过 "use client" 指令导出。

使用 App Router 设置 MUI Base

MUI Base 使您可以自由选择自己的样式解决方案,因此设置 Next.js App Router 项目在很大程度上取决于您的选择。本指南涵盖 Tailwind CSS、Emotion 和其他 CSS-in-JS 解决方案,如 styled-components。

Tailwind CSS

按照关于使用 Next.js 的 Tailwind CSS 指南,并确保将 app 目录和其他目录添加到 tailwind.config.js,如下所示

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}'
    // or if not using the `src` directory:
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

有关使用 MUI Base 和 Tailwind CSS 的 Next.js 13 应用程序的完整工作演示,请参阅此示例仓库

Emotion

如果您使用 Emotion,或基于 Emotion 的东西(如 MUI System),请创建一个自定义 ThemeRegistry 组件,该组件结合了 Emotion CacheProvider、Material UI ThemeProvider 和来自 next/navigationuseServerInsertedHTML Hook,如下所示

// app/ThemeRegistry.tsx
'use client';
import createCache from '@emotion/cache';
import { useServerInsertedHTML } from 'next/navigation';
import { CacheProvider, ThemeProvider } from '@emotion/react';
import theme from '/path/to/your/theme';

// This implementation is from emotion-js
// https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902
export default function ThemeRegistry(props) {
  const { options, children } = props;

  const [{ cache, flush }] = React.useState(() => {
    const cache = createCache(options);
    cache.compat = true;
    const prevInsert = cache.insert;
    let inserted: string[] = [];
    cache.insert = (...args) => {
      const serialized = args[1];
      if (cache.inserted[serialized.name] === undefined) {
        inserted.push(serialized.name);
      }
      return prevInsert(...args);
    };
    const flush = () => {
      const prevInserted = inserted;
      inserted = [];
      return prevInserted;
    };
    return { cache, flush };
  });

  useServerInsertedHTML(() => {
    const names = flush();
    if (names.length === 0) {
      return null;
    }
    let styles = '';
    for (const name of names) {
      styles += cache.inserted[name];
    }
    return (
      <style
        key={cache.key}
        data-emotion={`${cache.key} ${names.join(' ')}`}
        dangerouslySetInnerHTML={{
          __html: styles,
        }}
      />
    );
  });

  return (
    <CacheProvider value={cache}>
      <ThemeProvider theme={theme}>{children}</ThemeProvider>
    </CacheProvider>
  );
}

// app/layout.js
export default function RootLayout(props) {
  return (
    <html lang="en">
      <body>
        <ThemeRegistry options={{ key: 'mui' }}>{props.children}</ThemeRegistry>
      </body>
    </html>
  );
}

如果您需要进一步覆盖主题样式(例如使用 CSS Modules),Emotion 为 createCache 提供了 prepend: true 选项来反转注入顺序,因此自定义样式可以覆盖主题,而无需使用 !important

目前,prepend 无法在 App Router 中可靠地工作,但您可以通过将 Emotion 样式包装在 CSS @layer 中来解决此问题,并对上面的代码片段进行修改

 useServerInsertedHTML(() => {
   const names = flush();
   if (names.length === 0) {
     return null;
   }
   let styles = '';
   for (const name of names) {
     styles += cache.inserted[name];
   }
   return (
     <style
       key={cache.key}
       data-emotion={`${cache.key} ${names.join(' ')}`}
       dangerouslySetInnerHTML={{
-        __html: styles,
+        __html: options.prepend ? `@layer emotion {${styles}}` : styles,
       }}
     />
   );
 });

其他 CSS-in-JS 库

要将 Next.js 与 MUI Base 和 styled-components 或其他 CSS-in-JS 解决方案一起使用,请按照关于 CSS-in-JS 的 Next.js 文档进行操作。

自定义

为插槽 props 使用回调

MUI Base 中常见的自定义方法是在 slotProps 中将回调传递给插槽,以便应用动态 props。例如,您可能希望在禁用按钮时通过应用不同的类来更改背景颜色

// page.tsx

export default function Page() {
  return (
    <React.Fragment>
      {/* Next.js won't render this button without 'use-client'*/}
      <Button
        slotProps={{
          root: (ownerState: ButtonOwnerState) => ({
            className: ownerState.disabled ? 'bg-gray-400' : 'bg-blue-400',
          }),
        }}
      >
        Submit
      </Button>

      {/* Next.js can render this */}
      <Button
        slotProps={{
          root: {
            className: 'bg-gray-400',
          },
        }}
      >
        Return
      </Button>
    </React.Fragment>
  );
}

不幸的是,这在 Server Component 中不起作用,因为函数 props 是不可序列化的。相反,Next.js 团队建议将这些组件“向下移动树”,以避免此问题并提高整体性能。