Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
432 lines
13 KiB
Markdown
432 lines
13 KiB
Markdown
# Next.js integration
|
||
|
||
<p class="description">Learn how to use Material UI with Next.js.</p>
|
||
|
||
## App Router
|
||
|
||
This section walks through the Material UI integration with the Next.js [App Router](https://nextjs.org/docs/app), an evolution of the [Pages Router](#pages-router), and, currently, the recommended way of building new Next.js applications starting from version 13.
|
||
|
||
### Installing the dependencies
|
||
|
||
Start by ensuring that you already have `@mui/material` and `next` installed.
|
||
Then, run one of the following commands to install the dependencies:
|
||
|
||
<codeblock storageKey="package-manager">
|
||
|
||
```bash npm
|
||
npm install @mui/material-nextjs @emotion/cache
|
||
```
|
||
|
||
```bash pnpm
|
||
pnpm add @mui/material-nextjs @emotion/cache
|
||
```
|
||
|
||
```bash yarn
|
||
yarn add @mui/material-nextjs @emotion/cache
|
||
```
|
||
|
||
</codeblock>
|
||
|
||
### Configuration
|
||
|
||
Inside `app/layout.tsx`, import the `AppRouterCacheProvider` and wrap all elements under the `<body>` with it:
|
||
|
||
```diff title="app/layout.tsx"
|
||
+import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
|
||
// or `v1X-appRouter` if you are using Next.js v1X
|
||
|
||
export default function RootLayout(props) {
|
||
return (
|
||
<html lang="en">
|
||
<body>
|
||
+ <AppRouterCacheProvider>
|
||
{props.children}
|
||
+ </AppRouterCacheProvider>
|
||
</body>
|
||
</html>
|
||
);
|
||
}
|
||
```
|
||
|
||
:::info
|
||
The `AppRouterCacheProvider` component is responsible for collecting the CSS generated by MUI System on the server, as Next.js is streaming chunks of the .html page to the client.
|
||
|
||
While it's not required to use the `AppRouterCacheProvider` component, it's recommended to use it to ensure that the styles are appended to the `<head>` and not rendering in the `<body>`.
|
||
See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 for why it's better.
|
||
:::
|
||
|
||
#### Custom cache (optional)
|
||
|
||
Use the `options` prop to override the default [cache options](https://emotion.sh/docs/@emotion/cache#options)—for example, the code snippet below shows how to change the CSS key to `css` (the default is `mui`):
|
||
|
||
```diff
|
||
<AppRouterCacheProvider
|
||
+ options={{ key: 'css' }}
|
||
>
|
||
{children}
|
||
</AppRouterCacheProvider>
|
||
```
|
||
|
||
### Font optimization
|
||
|
||
To integrate [Next.js font optimization](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) with Material UI, create a new file with the `'use client';` directive.
|
||
Then create a theme using `var(--font-roboto)` as a value for the `typography.fontFamily` field.
|
||
|
||
```js title="src/theme.ts"
|
||
'use client';
|
||
import { createTheme } from '@mui/material/styles';
|
||
|
||
const theme = createTheme({
|
||
typography: {
|
||
fontFamily: 'var(--font-roboto)',
|
||
},
|
||
});
|
||
|
||
export default theme;
|
||
```
|
||
|
||
Finally, in `src/app/layout.tsx`, pass the theme to the `ThemeProvider`:
|
||
|
||
```diff title="app/layout.tsx"
|
||
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-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" className={roboto.variable}>
|
||
<body>
|
||
<AppRouterCacheProvider>
|
||
+ <ThemeProvider theme={theme}>
|
||
{children}
|
||
+ </ThemeProvider>
|
||
</AppRouterCacheProvider>
|
||
</body>
|
||
</html>
|
||
);
|
||
}
|
||
```
|
||
|
||
To learn more about theming, check out the [theming guide](/material-ui/customization/theming/) page.
|
||
|
||
### CSS theme variables
|
||
|
||
To use [CSS theme variables](/material-ui/customization/css-theme-variables/overview/), enable the `cssVariables` flag:
|
||
|
||
```diff title="src/theme.ts"
|
||
'use client';
|
||
const theme = createTheme({
|
||
+ cssVariables: true,
|
||
});
|
||
```
|
||
|
||
Learn more about [the advantages of CSS theme variables](/material-ui/customization/css-theme-variables/overview/#advantages) and how to [prevent SSR flickering](/material-ui/customization/css-theme-variables/configuration/#preventing-ssr-flickering).
|
||
|
||
### Using other styling solutions
|
||
|
||
If you are using a styling solution other than Emotion to customize Material UI components, set `enableCssLayer: true` in the `options` prop:
|
||
|
||
```js
|
||
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
|
||
```
|
||
|
||
This option ensures that the styles generated by Material UI will be wrapped in a CSS `@layer mui` rule, which is overridden by anonymous layer styles when using Material UI with CSS Modules, Tailwind CSS, or even plain CSS without using `@layer`.
|
||
|
||
To learn more about it, see [the MDN CSS layer documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@layer).
|
||
|
||
### Next.js v16 Client Component restriction
|
||
|
||
If you encounter `Functions cannot be passed directly to Client Components` error from passing Next.js Link to Material UI `component` prop, you need to create a wrapper component with `use client` directive like the following:
|
||
|
||
```tsx title="src/components/Link.tsx"
|
||
'use client';
|
||
import Link, { LinkProps } from 'next/link';
|
||
|
||
export default Link;
|
||
```
|
||
|
||
Then, replace the Next.js Link with the wrapper component:
|
||
|
||
```diff title="src/app/page.tsx"
|
||
- import Link from 'next/link';
|
||
+ import Link from '../components/Link';
|
||
...
|
||
<Button component={Link} href="/about" variant="contained">
|
||
Go to About Page
|
||
</Button>
|
||
```
|
||
|
||
## Pages Router
|
||
|
||
This section walks through the Material UI integration with the Next.js [Pages Router](https://nextjs.org/docs/pages/building-your-application), for both [Server-side Rendering](https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering) (SSR) and [Static Site Generation](https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation) (SSG).
|
||
|
||
### Installing the dependencies
|
||
|
||
Start by ensuring that you already have `@mui/material` and `next` installed.
|
||
Then, run one of the following commands to install the dependencies:
|
||
|
||
<codeblock storageKey="package-manager">
|
||
|
||
```bash npm
|
||
npm install @mui/material-nextjs @emotion/cache @emotion/server
|
||
```
|
||
|
||
```bash pnpm
|
||
pnpm add @mui/material-nextjs @emotion/cache @emotion/server
|
||
```
|
||
|
||
```bash yarn
|
||
yarn add @mui/material-nextjs @emotion/cache @emotion/server
|
||
```
|
||
|
||
</codeblock>
|
||
|
||
### Configuration
|
||
|
||
Inside the `pages/_document.tsx` file:
|
||
|
||
- Import `documentGetInitialProps` and use it as the Document's `getInitialProps`.
|
||
- Import `DocumentHeadTags` and render it inside the `<Head>`.
|
||
|
||
```diff title="pages/_document.tsx"
|
||
+import {
|
||
+ DocumentHeadTags,
|
||
+ documentGetInitialProps,
|
||
+} from '@mui/material-nextjs/v15-pagesRouter';
|
||
// or `v1X-pagesRouter` if you are using Next.js v1X
|
||
|
||
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;
|
||
+};
|
||
```
|
||
|
||
Then, inside `pages/_app.tsx`, import the `AppCacheProvider` component and render it as the root element:
|
||
|
||
```diff title="pages/_app.tsx"
|
||
+import { AppCacheProvider } from '@mui/material-nextjs/v15-pagesRouter';
|
||
// Or `v1X-pages` if you are using Next.js v1X
|
||
|
||
export default function MyApp(props) {
|
||
return (
|
||
+ <AppCacheProvider {...props}>
|
||
<Head>
|
||
...
|
||
</Head>
|
||
...
|
||
+ </AppCacheProvider>
|
||
);
|
||
}
|
||
```
|
||
|
||
:::info
|
||
The `AppCacheProvider` component is responsible for collecting the CSS generated by MUI System on the server, as Next.js is rendering the .html page to the client.
|
||
|
||
While it's not required to use the `AppCacheProvider` component, it's recommended to use it to ensure that the styles are appended to the `<head>` and not rendering in the `<body>`.
|
||
See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 for why it's better.
|
||
:::
|
||
|
||
#### Custom cache (optional)
|
||
|
||
To use a custom [Emotion cache](https://emotion.sh/docs/@emotion/cache), pass it to the `emotionCache` property in `_document.tsx`:
|
||
|
||
```diff title="pages/_document.tsx"
|
||
...
|
||
|
||
MyDocument.getInitialProps = async (ctx) => {
|
||
const finalProps = await documentGetInitialProps(ctx, {
|
||
+ emotionCache: createCustomCache(),
|
||
});
|
||
return finalProps;
|
||
};
|
||
```
|
||
|
||
#### Cascade layers (optional)
|
||
|
||
To enable [cascade layers](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers) (`@layer`), create a new cache with `enableCssLayer: true` and pass it to the `emotionCache` property in both `_document.tsx` and `_app.tsx`:
|
||
|
||
```diff title="pages/_document.tsx"
|
||
+import { createEmotionCache } from '@mui/material-nextjs/v15-pagesRouter';
|
||
...
|
||
|
||
MyDocument.getInitialProps = async (ctx) => {
|
||
const finalProps = await documentGetInitialProps(ctx, {
|
||
+ emotionCache: createEmotionCache({ enableCssLayer: true }),
|
||
});
|
||
return finalProps;
|
||
};
|
||
```
|
||
|
||
```diff title="pages/_app.tsx"
|
||
+import { createEmotionCache } from '@mui/material-nextjs/v15-pagesRouter';
|
||
...
|
||
|
||
const clientCache = createEmotionCache({ enableCssLayer: true });
|
||
|
||
+ export default function MyApp({ emotionCache = clientCache }) {
|
||
return (
|
||
+ <AppCacheProvider emotionCache={emotionCache}>
|
||
<Head>
|
||
...
|
||
</Head>
|
||
...
|
||
</AppCacheProvider>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### App enhancement (optional)
|
||
|
||
Pass an array to the `plugins` property to enhance the app with additional features, like server-side-rendered styles if you're using JSS and styled-components.
|
||
|
||
Each plugin must have the following properties:
|
||
|
||
- `enhanceApp`: a higher-order component that receives the `App` component and returns a new app component.
|
||
- `resolveProps`: a function that receives the initial props and returns a new props object.
|
||
|
||
When run, `enhanceApp` from each plugin is called first, from top to bottom, and then the process is repeated for `resolveProps`.
|
||
|
||
```js
|
||
import { ServerStyleSheet } from 'styled-components';
|
||
|
||
MyDocument.getInitialProps = async (ctx) => {
|
||
const jssSheets = new JSSServerStyleSheets();
|
||
const styledComponentsSheet = new ServerStyleSheet();
|
||
|
||
try {
|
||
const finalProps = await documentGetInitialProps(ctx, {
|
||
emotionCache: createEmotionCache(),
|
||
plugins: [
|
||
{
|
||
// styled-components
|
||
enhanceApp: (App) => (props) =>
|
||
styledComponentsSheet.collectStyles(<App {...props} />),
|
||
resolveProps: async (initialProps) => ({
|
||
...initialProps,
|
||
styles: [
|
||
styledComponentsSheet.getStyleElement(),
|
||
...initialProps.styles,
|
||
],
|
||
}),
|
||
},
|
||
{
|
||
// JSS
|
||
enhanceApp: (App) => (props) => jssSheets.collect(<App {...props} />),
|
||
resolveProps: async (initialProps) => {
|
||
const css = jssSheets.toString();
|
||
return {
|
||
...initialProps,
|
||
styles: [
|
||
...initialProps.styles,
|
||
<style
|
||
id="jss-server-side"
|
||
key="jss-server-side"
|
||
// eslint-disable-next-line react/no-danger
|
||
dangerouslySetInnerHTML={{ __html: css }}
|
||
/>,
|
||
<style id="insertion-point-jss" key="insertion-point-jss" />,
|
||
],
|
||
};
|
||
},
|
||
},
|
||
],
|
||
});
|
||
return finalProps;
|
||
} finally {
|
||
styledComponentsSheet.seal();
|
||
}
|
||
};
|
||
```
|
||
|
||
### TypeScript
|
||
|
||
If you are using TypeScript, add `DocumentHeadTagsProps` to the Document's props interface:
|
||
|
||
```diff
|
||
+import type { DocumentHeadTagsProps } from '@mui/material-nextjs/v15-pagesRouter';
|
||
// or `v1X-pagesRouter` if you are using Next.js v1X
|
||
|
||
+export default function MyDocument(props: DocumentProps & DocumentHeadTagsProps) {
|
||
...
|
||
}
|
||
```
|
||
|
||
### Font optimization
|
||
|
||
To integrate [Next.js font optimization](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) with Material UI, open `pages/_app.tsx` and create a theme using `var(--font-roboto)` as a value for the `typography.fontFamily` field.
|
||
|
||
```diff title="pages/_app.tsx"
|
||
import * as React from 'react';
|
||
import Head from 'next/head';
|
||
import { AppProps } from 'next/app';
|
||
import { AppCacheProvider } from '@mui/material-nextjs/v15-pagesRouter';
|
||
+import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||
+import { Roboto } from 'next/font/google';
|
||
|
||
+const roboto = Roboto({
|
||
+ weight: ['300', '400', '500', '700'],
|
||
+ subsets: ['latin'],
|
||
+ display: 'swap',
|
||
+ variable: '--font-roboto',
|
||
+});
|
||
|
||
+const theme = createTheme({
|
||
+ typography: {
|
||
+ fontFamily: 'var(--font-roboto)',
|
||
+ },
|
||
+});
|
||
|
||
export default function MyApp(props: AppProps) {
|
||
const { Component, pageProps } = props;
|
||
return (
|
||
<AppCacheProvider {...props}>
|
||
<Head>...</Head>
|
||
+ <ThemeProvider theme={theme}>
|
||
+ <main className={roboto.variable}>
|
||
<Component {...pageProps} />
|
||
+ </main>
|
||
+ </ThemeProvider>
|
||
</AppCacheProvider>
|
||
);
|
||
}
|
||
```
|
||
|
||
To learn more about theming, check out the [Theming guide](/material-ui/customization/theming/).
|
||
|
||
### CSS theme variables
|
||
|
||
To use [CSS theme variables](/material-ui/customization/css-theme-variables/overview/), enable the `cssVariables` flag:
|
||
|
||
```diff title="src/theme.ts"
|
||
'use client';
|
||
const theme = createTheme({
|
||
+ cssVariables: true,
|
||
});
|
||
```
|
||
|
||
Learn more about [the advantages of CSS theme variables](/material-ui/customization/css-theme-variables/overview/#advantages) and how to [prevent SSR flickering](/material-ui/customization/css-theme-variables/configuration/#preventing-ssr-flickering).
|