はじめに
Material UIはReactのUIコンポーネントライブラリの1つです。名のとおりGoogleのMaterial Designを実装するUIコンポーネントを提供しています(提供されるのは最新のMaterial Design 3ではなくMaterial Design 2です)。
その洗礼された豊富なコンポーネントを手軽に、独自のデザインシステムにカスタマイズしながら利用できる優れたライブラリです。
そんなMaterial UIですが、バージョン5.15.0でNext.jsと統合するためのライブラリとして、@mui/material-nextjsの提供を開始しました。
この記事では@mui/material-nextjsを用いたNext.jsのApp Routerとの統合について解説します。
統合
Nextjsの環境準備
npx create-next-appでNextjsの最新のプロジェクトを作成します。作成のための質問には以下のように答えました。装飾はMaterial UIで行いたいのでTailwind CSSをNoに選択しました。

装飾をMaterial UIをベースに行わせるために、CSSやCSS Modulesで書かれている部分を外していきます。CSSファイルは削除して、tsxファイルはそれぞれ以下のように変更します。
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}
export default function Home() {
return <></>
}
Material UIを利用可能に
次に、Material UIに関連するライブラリを導入します。
npm install @mui/material @emotion/react @emotion/styled @mui/material-nextjs @emotion/cache
すでにMaterial UIの環境がある場合は下の2つだけで問題ないです。
npm install @mui/material-nextjs @emotion/cache
パッケージ類の準備は完了なのでlayout.tsxとpage.tsxを触っていきます。
まずは、layout.tsxでbody以下の要素に対するProviderを設置します。
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<AppRouterCacheProvider>
{children}
</AppRouterCacheProvider>
</body>
</html>
)
}
Nextjsのバージョン14以上を利用の方は上記のように、13以上の場合は'@mui/material-nextjs/v13-appRouter'から同じようにProviderを取得してください。
その後、Material UIを使ったコードをpage.tsxで利用可能になります。
import { Button, Stack, TextField, Typography } from "@mui/material";
export default function Home() {
return (
<Stack height="100lvh" justifyContent="center" alignItems="center" gap="32px">
<Typography id="login_heading" variant="h1" fontSize="1.5rem">ログインフォーム</Typography>
<Stack component="form" width={560} gap="24px" aria-labelledby="login_heading">
<TextField label="メールアドレス" />
<TextField label="パスワード" />
<Button variant="contained">ログイン</Button>
</Stack>
</Stack>
);
}
出力は以下の通りMaterial UIで装飾されたものです。

これで統合は完了です。
AppRouterCacheProvider
@mui/material-nextjsを利用してApp RouterにMaterial UIを統合するためにはAppRouterCacheProviderをルートに設置するだけでした。
AppRouterCacheProviderはReactによって生成されたスタイルを@emotion/cacheを利用して収集して、useServerInsertedHTMLでheadに追加しています。
Material UIで使われているEmotionについての解説はありませんが、Nextjsのドキュメントではstyled-jsxやstyled-componentなどのCSS in JSについてuseServerInsertedHTMLを用いて同様の処理が書かれています。
オプション
AppRouterCacheProviderにはchildrenの他に2つのpropsをとります。
1つ目はemotion/cacheのcreaeCacheに渡すための設定に加えてenableCssLayerをキーとした真偽値を任意のオブジェクトとしてoptionsという名前で持ちます。
creaeCacheに渡すための設定はCSP(Content Security Policy)が有効な時に使われるnonce属性や、前処理中にStylisによって実行されるプラグインを渡すstylisPlugins、クラス名のプレフィックスとして利用されるkey、スタイルタグを挿入するDOMを指定するcontainerなどを渡します(参考)。
enableCssLayerは真偽値を値として取り、trueにした場合は生成したスタイルを@layer muiで囲むようになります。これによってEmotionを利用せずに、Tailwind CSSやCSS Modulesなどを用いてMaterial UIのスタイリングを上書きすることが可能となります。@layerは詳細度をより細かく制御するためのアットルールです。
2つ目はCacheProviderです。 デフォルトではemotion/reactのCacheProviderが利用されていますが、他のEmotionに対するキャッシュプロバイダを渡せます。
さいごに
NextjsのApp RouterにMaterial UIを統合させる方法を紹介しました。Material UIはこれまでApp Routerで利用するためには例にあるように複雑なコンポーネントを手前で組み込んで利用する必要がありましたが、@mui/material-nextjsの登場で解決しました。
未だ、ほとんどのコンポーネントは'use client'が貼られており、クライアントコンポーネントとして扱うことになってしまいますが、これまでより少し使いやすくなったのではないかなと思います。