🐈

Next.js で MUI を使用するベストプラクティス

2024/09/24に公開

MUI はSSRを考慮して設計されていますが、正しく統合されていることが重要です。

see: Material UI on the server

必要な CSS を提供することが重要です。そうしないと、ページが HTML のみでレンダリングされ、クライアントによって CSS が挿入されるのを待つため、画面がちらつく(FOUC)可能性があります。

以下の手順が必要です。

  • リクエストごとに、新しい emotion キャッシュインスタンスを作成します。
    • Next.jsでMaterial UIを使用する場合、SSRとCSRの両方でスタイルを正しく処理するために、Emotionのキャッシュ機構が重要。
  • サーバーサイドコレクターを使用して、React ツリーをレンダリングします。
    • サーバーサイドレンダリング時に生成されたスタイルを収集し、HTMLに注入する仕組み
      • AppRouterCacheProvide(Next.js >= 13), AppCacheProvider(Next.js < 13)

1. 必要な依存関係をインストールする

  • @mui/material
  • @emotion/react
  • @emotion/styled
  • @mui/material-nextjs
  • @emotion/cache

see: Installing the dependencies

2. App Router を使用する

Next.js バージョン 13 以降では、App Router が推奨されます。

app/layout.tsx で AppRouterCacheProvider をインポートし、<body> の下のすべての要素をラップします。これにより、MUI System によって生成された CSS がサーバー上で収集され、 <head> に追加されます。[1]

app/layout.tsx
import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';

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

AppRouterCacheProvider コンポーネントは、Emotion のキャッシュ機構を利用することで、サーバーサイドレンダリングで生成された Material UI のスタイルをクライアントサイドで再利用できるようにし、FOUC[2]を効果的に防止します。

Emotion 以外のスタイリングソリューションを使用する場合

AppRouterCacheProvider の options prop に enableCssLayer: true を設定します。[3]

<AppRouterCacheProvider options={{ enableCssLayer: true }}>

3. Pages Router を使用する (Next.js v13 以前の場合)

pages/_document.tsx で、 documentGetInitialProps をインポートし、Document の getInitialProps として使用します。また、 DocumentHeadTags をインポートし、<Head> 内でレンダリングします。[4]

pages/_document.tsx
import {
  DocumentHeadTags,
  documentGetInitialProps,
} from '@mui/material-nextjs/v14-pagesRouter';

export default function MyDocument(props) {
  return (
    <Html lang="en">
      <Head>
        <DocumentHeadTags {...props} />
        ...
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

MyDocument.getInitialProps = async (ctx) => {
  const finalProps = await documentGetInitialProps(ctx);
  return finalProps;
};

pages/_app.tsx で、 AppCacheProvider コンポーネントをインポートし、ルート要素としてレンダリングします。

pages/_app.tsx
import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter';

export default function MyApp(props) {
  return (
    <AppCacheProvider {...props}>
      <Head>
        ...
      </Head>
      ...
    </AppCacheProvider>
  );
}

4. フォントの最適化

Next.js のフォントの最適化を MUI と統合するには、'use client';ディレクティブを使用して新しいファイルを作成します。

typography.fontFamily フィールドの値として var(--font-roboto) を使用してテーマを作成します。[5]

src/app/layout.tsx で、テーマを ThemeProvider に渡します。

src/theme.ts
'use client';

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

const theme = createTheme({
  typography: {
    fontFamily: 'var(--font-roboto)',
  },
});

export default theme;
src/app/layout.tsx
import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';
import { Roboto } from 'next/font/google';
import { ThemeProvider } from '@mui/material/styles';
import theme from '../theme';

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

export default function RootLayout(props) {
  const { children } = props;
  return (
    <html lang="en">
      <body className={roboto.variable}>
        <AppRouterCacheProvider>
          <ThemeProvider theme={theme}>
            {children}
          </ThemeProvider>
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

5. CSS テーマ変数 CSS テーマ変数を使用するには、 cssVariables フラグを有効にします。

src/theme.ts
'use client';

const theme = createTheme({
  cssVariables: true,
});

see Next.js + Material UI v5 でフロントエンドアプリケーションを作成する

状況に応じてnext/linkまたは@mui/material/Linkを返す独自の Link コンポーネントを作成します。

URLが内部ページの場合はnext/linkを、外部ページの場合は @mui/material/Linkを使用します。

脚注
  1. https://mui.com/material-ui/integrations/nextjs/#configuration ↩︎

  2. Flash Of Unstyled Contentの略語で、スタイルが適用されていないコンテンツが一瞬表示されてしまう現象のこと ↩︎

  3. https://mui.com/material-ui/integrations/nextjs/#using-other-styling-solutions ↩︎

  4. https://mui.com/material-ui/integrations/nextjs/#pages-router ↩︎

  5. https://mui.com/material-ui/integrations/nextjs/#font-optimization-2 ↩︎

Discussion