Next.js で MUI を使用するベストプラクティス
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)
- サーバーサイドレンダリング時に生成されたスタイルを収集し、HTMLに注入する仕組み
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]
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]
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 コンポーネントをインポートし、ルート要素としてレンダリングします。
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 に渡します。
'use client';
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
typography: {
fontFamily: 'var(--font-roboto)',
},
});
export default theme;
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 フラグを有効にします。
'use client';
const theme = createTheme({
cssVariables: true,
});
6. next/link と @mui/material/Link の統合
see Next.js + Material UI v5 でフロントエンドアプリケーションを作成する
状況に応じてnext/link
または@mui/material/Link
を返す独自の Link コンポーネントを作成します。
URLが内部ページの場合はnext/link
を、外部ページの場合は @mui/material/Link
を使用します。
Discussion