Material UIのバージョン5.14.0からNext.jsのApp Routerがサポートされたので、Material UIを使用するために”use client”を書く必要はありません。
このきじはこちらのgitリポジトリを参考にしています。
目次
muiをインストール
まずはmuiをインストールします。
npm install @mui/material @emotion/react @emotion/styled
yarn add @mui/material @emotion/react @emotion/styled
カスタム ThemeRegistry コンポーネントなどを作成
"use client";
import * as React from "react";
import createCache from "@emotion/cache";
import { useServerInsertedHTML } from "next/navigation";
import { CacheProvider as DefaultCacheProvider } from "@emotion/react";
import type {
EmotionCache,
Options as OptionsOfCreateCache,
} from "@emotion/cache";
export type NextAppDirEmotionCacheProviderProps = {
/** This is the options passed to createCache() from 'import createCache from "@emotion/cache"' */
options: Omit<OptionsOfCreateCache, "insertionPoint">;
/** By default <CacheProvider /> from 'import { CacheProvider } from "@emotion/react"' */
CacheProvider?: (props: {
value: EmotionCache;
children: React.ReactNode;
}) => React.JSX.Element | null;
children: React.ReactNode;
};
// Adapted from https://github.com/garronej/tss-react/blob/main/src/next/appDir.tsx
export default function NextAppDirEmotionCacheProvider(
props: NextAppDirEmotionCacheProviderProps
) {
const { options, CacheProvider = DefaultCacheProvider, children } = props;
const [registry] = React.useState(() => {
const cache = createCache(options);
cache.compat = true;
const prevInsert = cache.insert;
let inserted: { name: string; isGlobal: boolean }[] = [];
cache.insert = (...args) => {
const [selector, serialized] = args;
if (cache.inserted[serialized.name] === undefined) {
inserted.push({
name: serialized.name,
isGlobal: !selector,
});
}
return prevInsert(...args);
};
const flush = () => {
const prevInserted = inserted;
inserted = [];
return prevInserted;
};
return { cache, flush };
});
useServerInsertedHTML(() => {
const inserted = registry.flush();
if (inserted.length === 0) {
return null;
}
let styles = "";
let dataEmotionAttribute = registry.cache.key;
const globals: {
name: string;
style: string;
}[] = [];
inserted.forEach(({ name, isGlobal }) => {
const style = registry.cache.inserted[name];
if (typeof style !== "boolean") {
if (isGlobal) {
globals.push({ name, style });
} else {
styles += style;
dataEmotionAttribute += ` ${name}`;
}
}
});
return (
<React.Fragment>
{globals.map(({ name, style }) => (
<style
key={name}
data-emotion={`${registry.cache.key}-global ${name}`}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style }}
/>
))}
{styles && (
<style
data-emotion={dataEmotionAttribute}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: styles }}
/>
)}
</React.Fragment>
);
});
return <CacheProvider value={registry.cache}>{children}</CacheProvider>;
}
"use client";
import * as React from "react";
import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import NextAppDirEmotionCacheProvider from "./EmotionCache";
import theme from "./theme";
export default function ThemeRegistry({
children,
}: {
children: React.ReactNode;
}) {
return (
<NextAppDirEmotionCacheProvider options={{ key: "mui" }}>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
{children}
</ThemeProvider>
</NextAppDirEmotionCacheProvider>
);
}
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({
palette: {
mode: "light",
},
typography: {
fontFamily: roboto.style.fontFamily,
},
components: {
MuiAlert: {
styleOverrides: {
root: ({ ownerState }) => ({
...(ownerState.severity === "info" && {
backgroundColor: "#60a5fa",
}),
}),
},
},
},
});
export default theme;
import "./globals.css";
import * as React from "react";
import ThemeRegistry from "@/components/ThemeRegistry/ThemeRegistry";
export const metadata = {
title: "タイトル",
description: "詳細説明",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ThemeRegistry>{children}</ThemeRegistry>
</body>
</html>
);
}
コメント