|
| 1 | +# Material UI and Next.js |
| 2 | + |
| 3 | +Version 1.0.0 |
| 4 | + |
| 5 | +> Note: This document is for agents and LLMs integrating Material UI with Next.js. Source: `docs/data/material/integrations/nextjs/nextjs.md` and related integration docs in this repository. |
| 6 | +
|
| 7 | +--- |
| 8 | + |
| 9 | +## Abstract |
| 10 | + |
| 11 | +Material UI uses Emotion for styles. On Next.js you must wire an Emotion cache so SSR and streaming produce correct CSS (prefer injecting styles into `head` instead of only `body`). The `@mui/material-nextjs` package supplies `AppRouterCacheProvider` (App Router) and `AppCacheProvider` / `DocumentHeadTags` (Pages Router). Material UI components ship as client components (`"use client"`); they still SSR but are not React Server Components. Match the package import suffix (for example `v15-appRouter`) to your Next.js major version. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Table of contents |
| 16 | + |
| 17 | +1. [App Router (recommended)](#app-router-recommended) |
| 18 | +2. [Pages Router](#pages-router) |
| 19 | +3. [Fonts (`next/font`)](#fonts-nextfont) |
| 20 | +4. [CSS theme variables and SSR](#css-theme-variables-and-ssr) |
| 21 | +5. [Other styling stacks (CSS layers)](#other-styling-stacks-css-layers) |
| 22 | +6. [Next.js Link and `component` prop](#nextjs-link-and-component-prop) |
| 23 | +7. [Further reading](#further-reading) |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## App Router (recommended) |
| 28 | + |
| 29 | +### Dependencies |
| 30 | + |
| 31 | +Have `@mui/material` and `next` installed, then add: |
| 32 | + |
| 33 | +- `@mui/material-nextjs` |
| 34 | +- `@emotion/cache` |
| 35 | + |
| 36 | +Example: `pnpm add @mui/material-nextjs @emotion/cache` |
| 37 | + |
| 38 | +### Root layout |
| 39 | + |
| 40 | +In `app/layout.tsx`, wrap everything under `<body>` with `AppRouterCacheProvider` from the entry that matches your Next major, for example: |
| 41 | + |
| 42 | +`import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';` |
| 43 | + |
| 44 | +(Use the `v1X-appRouter` path that matches your Next.js version if not on v15.) |
| 45 | + |
| 46 | +Why: it collects CSS from MUI System during server rendering and streaming so styles attach predictably; it is recommended so styles go to `<head>` instead of only `<body>`. See [Next.js integration—Configuration](https://mui.com/material-ui/integrations/nextjs.md#configuration). |
| 47 | + |
| 48 | +### Optional cache `options` |
| 49 | + |
| 50 | +Pass `options` to `AppRouterCacheProvider` to override [Emotion cache options](https://emotion.sh/docs/@emotion/cache#options), for example `key: 'css'` (the default MUI key is `mui`). See [Next.js integration—Custom cache (optional)](https://mui.com/material-ui/integrations/nextjs.md#custom-cache-optional). |
| 51 | + |
| 52 | +### URL hooks and Suspense |
| 53 | + |
| 54 | +Dashboards and internal tools often combine MUI client components with URL-driven UI (filters, tabs, pagination) using `useSearchParams()` from `next/navigation`. |
| 55 | + |
| 56 | +Next.js expects a `<Suspense>` boundary around the part of the tree that uses `useSearchParams` (and similar patterns that opt the route into client-side rendering), otherwise you can get build failures or runtime errors about a missing Suspense boundary. |
| 57 | + |
| 58 | +Practical pattern: keep `app/.../page.tsx` as a server component when possible; render a client subtree that uses components such as `Table`, `Tabs`, or `TextField` and is tied to the query string inside `<Suspense>` from that server page. Do not use `fallback={null}` for UI that occupies layout space (toolbars, filters, and similar); it tends to cause layout shift when the client mounts. Use a fallback that matches the real layout (for example `Skeleton` with `Stack` or `Box` and the same `minHeight` and rough dimensions as the final UI). Full example: [Next.js integration—URL-driven UI and the Suspense boundary](https://mui.com/material-ui/integrations/nextjs.md#url-driven-ui-and-the-suspense-boundary). |
| 59 | + |
| 60 | +Official reference: [Next.js—`useSearchParams`](https://nextjs.org/docs/app/api-reference/functions/use-search-params) (static rendering and Suspense notes vary by major version). |
| 61 | + |
| 62 | +--- |
| 63 | + |
| 64 | +## Pages Router |
| 65 | + |
| 66 | +### Dependencies |
| 67 | + |
| 68 | +Add `@mui/material-nextjs`, `@emotion/cache`, and `@emotion/server`. |
| 69 | + |
| 70 | +Example: `pnpm add @mui/material-nextjs @emotion/cache @emotion/server` |
| 71 | + |
| 72 | +### `_document.tsx` |
| 73 | + |
| 74 | +- Import `DocumentHeadTags` and `documentGetInitialProps` from the `v15-pagesRouter` (or matching `v1X-pagesRouter`) entry. |
| 75 | +- Render `<DocumentHeadTags {...props} />` inside `<Head>`. |
| 76 | +- Assign `getInitialProps` to call `documentGetInitialProps`. |
| 77 | + |
| 78 | +### `_app.tsx` |
| 79 | + |
| 80 | +Wrap the app with `AppCacheProvider` from the same major entry (for example `v15-pagesRouter`). |
| 81 | + |
| 82 | +### Optional: custom cache and cascade layers |
| 83 | + |
| 84 | +- Pass a custom `emotionCache` into `documentGetInitialProps` options when needed. |
| 85 | +- For `@layer`, use `createEmotionCache({ enableCssLayer: true })` from `@mui/material-nextjs`, pass it from `_document` and align `_app` with the same cache pattern. See [Next.js integration—Cascade layers (optional)](https://mui.com/material-ui/integrations/nextjs.md#cascade-layers-optional). |
| 86 | + |
| 87 | +### TypeScript |
| 88 | + |
| 89 | +Extend `Document` props with `DocumentHeadTagsProps` from the same import path. See [Next.js integration—TypeScript](https://mui.com/material-ui/integrations/nextjs.md#typescript). |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +## Fonts (`next/font`) |
| 94 | + |
| 95 | +App Router: theme modules that call `createTheme` need `'use client'` when they are consumed from server components. Use `next/font/google` (or local fonts), set `variable: '--font-…'`, put `className={font.variable}` on `<html>` (or as in docs), and set `typography.fontFamily` to `'var(--font-…)'`. Wrap with `ThemeProvider` inside `AppRouterCacheProvider` as needed. |
| 96 | + |
| 97 | +Pages Router: similar pattern in `pages/_app.tsx` with `AppCacheProvider` and `ThemeProvider`. |
| 98 | + |
| 99 | +Details: [Next.js integration—Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization) (App) and [Next.js integration—Font optimization](https://mui.com/material-ui/integrations/nextjs.md#font-optimization-1) (Pages). |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +## CSS theme variables and SSR |
| 104 | + |
| 105 | +Enable `cssVariables: true` in `createTheme` when using [CSS theme variables](https://mui.com/material-ui/customization/css-theme-variables/overview.md). For SSR flicker and `InitColorSchemeScript`, follow [CSS theme variables—Preventing SSR flickering](https://mui.com/material-ui/customization/css-theme-variables/configuration.md#preventing-ssr-flickering) and [CSS theme variables overview—Advantages](https://mui.com/material-ui/customization/css-theme-variables/overview.md#advantages). Add `suppressHydrationWarning` to `<html>` when using `colorSchemes` — the color scheme attribute is written client-side on first render and will otherwise produce a React hydration mismatch. |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## Other styling stacks (CSS layers) |
| 110 | + |
| 111 | +If you combine MUI with Tailwind CSS, CSS Modules, or other global CSS, set `enableCssLayer: true` on `AppRouterCacheProvider`: |
| 112 | + |
| 113 | +`<AppRouterCacheProvider options={{ enableCssLayer: true }}>` |
| 114 | + |
| 115 | +That wraps MUI output in `@layer mui` so anonymous layers can override as intended. See [Next.js integration—Using other styling solutions](https://mui.com/material-ui/integrations/nextjs.md#using-other-styling-solutions) and [MDN—@layer](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@layer). |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +## Next.js Link and `component` prop |
| 120 | + |
| 121 | +Next.js v16: passing `next/link` directly into `component` can trigger "Functions cannot be passed directly to Client Components". Fix: a small client re-export: |
| 122 | + |
| 123 | +```tsx |
| 124 | +'use client'; |
| 125 | +import Link, { LinkProps } from 'next/link'; |
| 126 | +export default Link; |
| 127 | +``` |
| 128 | + |
| 129 | +Import that wrapper and use `component={Link}` on `Button` and similar. See [Next.js integration—Next.js v16 Client Component restriction](https://mui.com/material-ui/integrations/nextjs.md#nextjs-v16-client-component-restriction). |
| 130 | + |
| 131 | +Pages Router and theme-wide patterns: see [Routing libraries—Next.js Pages Router](https://mui.com/material-ui/integrations/routing.md#nextjs-pages-router) and the [material-ui-nextjs-pages-router-ts example](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-pages-router-ts). |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +## Further reading |
| 136 | + |
| 137 | +| Topic | Link | |
| 138 | +| :------------------------------- | :----------------------------------------------------------------------------------------------------- | |
| 139 | +| Full integration guide | [Next.js integration](https://mui.com/material-ui/integrations/nextjs.md) | |
| 140 | +| Example (App Router, TypeScript) | [material-ui-nextjs-ts](https://github.com/mui/material-ui/tree/master/examples/material-ui-nextjs-ts) | |
| 141 | +| Routing + Link adapters | [Routing libraries](https://mui.com/material-ui/integrations/routing.md) | |
| 142 | +| RSC vs SSR (terminology) | [React WG discussion](https://github.com/reactwg/server-components/discussions/4) | |
| 143 | + |
| 144 | +Import path cheat sheet: [reference.md](reference.md). |
0 commit comments