How to Integrate Material-UI with Next.js App Router Using @mui/material-nextjs

Use the AppRouterCacheProvider component from @mui/material-nextjs to create an Emotion cache that collects server-side CSS and streams style tags into the HTML response.

Material-UI provides a dedicated integration package @mui/material-nextjs that streamlines server-side rendering in Next.js 13+ applications using the App Router. This package exports the AppRouterCacheProvider component, which manages Emotion cache insertion during streaming SSR. According to the Material-UI source code, the provider wraps your application tree to ensure styles are correctly generated on the server and hydrated on the client.

Installation and Setup

First, install the integration package alongside Emotion cache (required peer dependency):

npm install @mui/material-nextjs @emotion/cache

Or using Yarn or pnpm:

yarn add @mui/material-nextjs @emotion/cache

# or

pnpm add @mui/material-nextjs @emotion/cache

Configuring the Root Layout

Wrap your application root with AppRouterCacheProvider in your app/layout.tsx file. Import the provider from the version-specific entry point that matches your Next.js version (e.g., v15-appRouter for Next.js 15).

import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AppRouterCacheProvider>
          {children}
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

How AppRouterCacheProvider Works

The integration relies on a thin wrapper around Emotion's caching system implemented in packages/mui-material-nextjs/src/v13-appRouter/appRouterV13.tsx. Understanding this architecture helps debug styling issues in complex applications.

Emotion Cache Architecture

The provider initializes an Emotion cache using createCache with a default key of mui. In the source code, the cache is configured with cache.compat = true to ensure generated class names match the classic Emotion format used by Material-UI components. This cache is then supplied to the React tree via Emotion's CacheProvider.

Server-Side Style Collection

During server-side rendering, the provider wraps the cache's insert method to record every style that is rendered. When Next.js calls useServerInsertedHTML (imported via the shim in packages/mui-material-nextjs/src/v13-appRouter/nextNavigation.cjs), the provider flushes the recorded styles and returns <style> elements that are injected into the streamed HTML. This mechanism ensures critical CSS arrives with the initial HTML payload.

CSS Cascade Layer Support

When the options.enableCssLayer flag is set to true, each style block is wrapped in @layer mui { … }. This allows developers to control style precedence when mixing Material-UI with Tailwind CSS, CSS Modules, or other styling solutions that utilize cascade layers.

Advanced Configuration

Enabling CSS Layers

To isolate Material-UI styles within a cascade layer—useful for resolving specificity conflicts with other CSS-in-JS solutions—pass the enableCssLayer option:

import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';

<AppRouterCacheProvider options={{ enableCssLayer: true, key: 'mui' }}>
  {children}
</AppRouterCacheProvider>

Font Optimization with Next.js

Combine the cache provider with Next.js 13+ font optimization by passing the font variable to your theme:

'use client';
import { createTheme } from '@mui/material/styles';
import { ThemeProvider } from '@mui/material';
import { Roboto } from 'next/font/google';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';

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 RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={roboto.variable}>
      <body>
        <AppRouterCacheProvider>
          <ThemeProvider theme={theme}>{children}</ThemeProvider>
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

Summary

  • @mui/material-nextjs provides AppRouterCacheProvider for seamless Material-UI integration with Next.js App Router.
  • The provider creates an Emotion cache in packages/mui-material-nextjs/src/v13-appRouter/appRouterV13.tsx that tracks server-rendered styles.
  • It uses useServerInsertedHTML from Next.js (via nextNavigation.cjs) to stream style tags into the HTML response.
  • Enable options.enableCssLayer to wrap styles in @layer mui for cascade control with Tailwind or CSS Modules.
  • Install alongside @emotion/cache as a peer dependency.

Frequently Asked Questions

What is the difference between @mui/material-nextjs and @emotion/cache?

@mui/material-nextjs is a thin integration wrapper that configures @emotion/cache specifically for Next.js App Router streaming SSR. While @emotion/cache provides the underlying caching mechanism, the Material-UI package handles the complex coordination between Emotion's style insertion and Next.js's useServerInsertedHTML hook, ensuring styles are correctly collected and injected during server-side rendering.

Why do I need to use a specific version import like v15-appRouter?

The versioned imports (e.g., v13-appRouter, v15-appRouter) correspond to Next.js major versions and ensure compatibility with specific App Router APIs. The underlying implementation in packages/mui-material-nextjs/src/v13-appRouter/appRouterV13.tsx remains consistent, but the entry points handle different Next.js dependency requirements and prevent client/server bundle mismatches.

How does enableCssLayer resolve style conflicts with Tailwind CSS?

When enableCssLayer is set to true, the provider wraps all Material-UI styles in @layer mui { ... }. This places MUI styles within a named cascade layer with lower priority than unlayered styles (like Tailwind's utility classes), allowing Tailwind utilities to override Material-UI component styles without requiring excessive specificity or !important flags.

Can I use a custom Emotion cache key with AppRouterCacheProvider?

Yes. While the default cache key is mui, you can override it via the options.key property. This is useful when running multiple Emotion instances or when integrating with other libraries that also use Emotion, though you should ensure the key matches across server and client to prevent hydration mismatches.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →