# How to Implement a Custom Material UI Nextjs Theme in the App Router for Optimal Performance

> Learn to implement a custom Material UI Nextjs theme in the App Router quickly. Enable cssVariables true and use ThemeProvider for zero-config SSR styling consistency.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Enable `cssVariables: true` in your MUI theme configuration and wrap your root layout with `ThemeProvider` and `InitColorSchemeScript` to achieve zero-config SSR styling consistency in Next.js 15.**

Integrating Material UI with Next.js requires careful handling of server-side rendering to prevent hydration mismatches and styling flashes. This guide demonstrates how to configure a custom Material UI Nextjs theme using the App Router architecture, leveraging CSS variables for optimal performance and consistent styling across server and client environments.

## Why CSS Variables Are Essential for Material UI Nextjs Performance

Material UI v5 and later support a `cssVariables` configuration that generates CSS custom properties instead of inline styles. This approach eliminates the need for runtime style injection on the client, reducing JavaScript execution time and preventing the flash of unstyled content (FOUC) during hydration.

In the Next.js repository, the benchmark application at [`turbopack/benchmark-apps/mui/theme.tsx`](https://github.com/vercel/next.js/blob/main/turbopack/benchmark-apps/mui/theme.tsx) demonstrates this configuration:

```tsx
import { createTheme } from '@mui/material/styles';
import { red } from '@mui/material/colors';

const theme = createTheme({
  cssVariables: true,
  palette: {
    primary: { main: '#556cd6' },
    secondary: { main: '#19857b' },
    error: { main: red.A400 },
  },
});

export default theme;

```

Setting `cssVariables: true` allows the browser to compute final CSS values without re-injecting style tags, providing the fastest First Contentful Paint and avoiding style-order mismatches between server and client renders.

## Configuring the Root Layout with ThemeProvider

The App Router architecture in Next.js 15 uses a root layout file ([`app/layout.tsx`](https://github.com/vercel/next.js/blob/main/app/layout.tsx)) that wraps all pages. Placing the `ThemeProvider` at this level ensures every route segment receives consistent theming while supporting both Server Components and Client Components.

Based on the implementation pattern found in [`turbopack/benchmark-apps/mui/index.tsx`](https://github.com/vercel/next.js/blob/main/turbopack/benchmark-apps/mui/index.tsx) and the routing logic in [`packages/next/src/client/components/app-router.tsx`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/components/app-router.tsx), your layout should import `InitColorSchemeScript` to prevent hydration mismatches:

```tsx
import React from 'react';
import ThemeProvider from '@mui/material/styles/ThemeProvider';
import CssBaseline from '@mui/material/CssBaseline';
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
import theme from '@/app/theme';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <ThemeProvider theme={theme}>
          <InitColorSchemeScript attribute="class" />
          <CssBaseline />
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

```

The `InitColorSchemeScript` component injects the initial color-scheme class (`light` or `dark`) on the server, ensuring the UI does not flash between modes when hydration occurs. This pattern aligns with the styled-components example in [`examples/with-styled-components/app/layout.tsx`](https://github.com/vercel/next.js/blob/main/examples/with-styled-components/app/layout.tsx), adapted for Material UI's specific requirements.

## Using Material UI Components in Server Components

One advantage of the App Router is that pages default to Server Components. Because the theme is provided at the layout level, Server-rendered components automatically receive the theme through CSS variables without requiring client-side JavaScript.

Create a page component that uses MUI components directly:

```tsx
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';

export default function HomePage() {
  return (
    <Box sx={{ p: 4 }}>
      <Typography variant="h4" gutterBottom>
        Welcome to My Material UI Nextjs App
      </Typography>
      <Button variant="contained" color="primary">
        Primary Action
      </Button>
    </Box>
  );
}

```

Since `cssVariables: true` generates static CSS custom properties, the server can render these components with correct styling without executing Emotion's runtime on the client. This eliminates the need for a custom `CacheProvider` and reduces bundle size.

## Implementing Dark Mode with Client Components

To allow users to toggle between light and dark modes, create a Client Component that interacts with the theme's color scheme. This component must use the `'use client'` directive because it manages browser state.

```tsx
'use client';
import * as React from 'react';
import { useColorScheme } from '@mui/material/styles';
import Switch from '@mui/material/Switch';
import Box from '@mui/material/Box';
import FormControlLabel from '@mui/material/FormControlLabel';

export default function ModeSwitch() {
  const { mode, setMode } = useColorScheme();

  const toggle = () => setMode(mode === 'dark' ? 'light' : 'dark');

  return (
    <Box sx={{ position: 'fixed', top: 16, right: 16 }}>
      <FormControlLabel
        control={<Switch checked={mode === 'dark'} onChange={toggle} />}
        label={mode === 'dark' ? 'Dark' : 'Light'}
      />
    </Box>
  );
}

```

Place this component inside your `RootLayout` after `CssBaseline`. The `InitColorSchemeScript` ensures the server renders the correct initial mode, while this Client Component handles subsequent user interactions without causing hydration mismatches.

## Summary

- **Enable CSS variables** by setting `cssVariables: true` in your `createTheme` configuration to eliminate runtime style injection and prevent hydration mismatches.
- **Place `ThemeProvider` in [`app/layout.tsx`](https://github.com/vercel/next.js/blob/main/app/layout.tsx)** to ensure consistent theming across all routes while supporting both Server and Client components.
- **Include `InitColorSchemeScript`** immediately inside the provider to inject the correct color-scheme class on the server and prevent flash of incorrect theme.
- **Use MUI components directly in Server components** when possible, as CSS variables allow the server to render styled HTML without client-side JavaScript.
- **Create Client components only for interactive features** like theme toggling, using the `useColorScheme` hook to manage state without breaking SSR consistency.

## Frequently Asked Questions

### Do I need Emotion's CacheProvider with Material UI Nextjs?

No. When you enable `cssVariables: true` in your theme configuration, Material UI generates CSS custom properties instead of relying on Emotion's runtime injection. This eliminates the need for a custom `CacheProvider` and reduces your JavaScript bundle size, as the styles are handled natively by the browser.

### Can I use Material UI components in Nextjs Server Components?

Yes. With the `cssVariables` configuration enabled, Material UI components can render correctly in Server Components because the theme values are embedded as CSS variables in the HTML. The `ThemeProvider` in your root layout supplies the theme context, allowing pages to use MUI components without marking them as client components, provided they don't use browser-only APIs or event handlers.

### How do I prevent flash of unstyled content with Material UI Nextjs?

Place the `InitColorSchemeScript` component immediately inside your `ThemeProvider` in the root layout. This script injects the correct color-scheme class (`light` or `dark`) during server-side rendering before React hydrates. Combined with `cssVariables: true`, this ensures the browser applies the correct styles immediately on page load, preventing any visual flash between default and user-preferred themes.

### Where should I place the ThemeProvider in the Nextjs App Router?

Always place the `ThemeProvider` in your root layout file at [`app/layout.tsx`](https://github.com/vercel/next.js/blob/main/app/layout.tsx). This ensures the provider wraps every page and route segment in your application. In the App Router architecture, the layout is a Server Component that executes once per route segment, making it the optimal location for theme context that must persist across client-side navigations while supporting initial server-side rendering.