This guide explains how to fix the internal_mutateStyles error when using Material-UI with Next.js App Router.
The error occurs because Material-UI's styling system tries to run on the server side, but Next.js App Router doesn't allow client-side functions to run during server-side rendering.
lib/theme-provider.tsx
'use client';
import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { createTheme } from '@mui/material/styles';
import { ReactNode } from 'react';
const theme = createTheme({
palette: {
mode: 'light',
},
});
interface ThemeProviderProps {
children: ReactNode;
}
export function ThemeProvider({ children }: ThemeProviderProps) {
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
{children}
</MuiThemeProvider>
);
}lib/urql-provider.tsx
'use client';
import { Provider as UrqlProvider, createClient } from 'urql';
import { ReactNode } from 'react';
const client = createClient({
url: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || 'https://your-graphql-endpoint.com/graphql',
});
interface GraphQLProviderProps {
children: ReactNode;
}
export function GraphQLProvider({ children }: GraphQLProviderProps) {
return (
<UrqlProvider value={client}>
{children}
</UrqlProvider>
);
}components/simfinity-wrapper.tsx
'use client';
import { ReactNode } from 'react';
import { ThemeProvider } from '@/lib/theme-provider';
import { GraphQLProvider } from '@/lib/urql-provider';
interface SimfinityWrapperProps {
children: ReactNode;
}
export function SimfinityWrapper({ children }: SimfinityWrapperProps) {
return (
<GraphQLProvider>
<ThemeProvider>
{children}
</ThemeProvider>
</GraphQLProvider>
);
}app/page.tsx
'use client';
import { Container, Typography, Box, Paper } from '@mui/material';
import { EntityForm, EntityTable } from 'simfinity-fe-components';
import { SimfinityWrapper } from '@/components/simfinity-wrapper';
export default function HomePage() {
return (
<SimfinityWrapper>
<Container maxWidth="lg" sx={{ py: 4 }}>
<Typography variant="h3" component="h1" gutterBottom>
Simfinity Components - Next.js Example
</Typography>
<EntityForm
listField="series"
action="create"
onSuccess={(data) => console.log('Form submitted:', data)}
/>
<EntityTable
listField="series"
onRowClick={(id) => console.log('Row clicked:', id)}
/>
</Container>
</SimfinityWrapper>
);
}For better SSR support, use Material-UI's recommended registry pattern:
npm install @emotion/cachelib/registry.tsx
'use client';
import { useServerInsertedHTML } from 'next/navigation';
import { useState } from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
export default function EmotionRegistry({
children,
}: {
children: React.ReactNode;
}) {
const [cache] = useState(() => {
const cache = createCache({ key: 'css' });
cache.compat = true;
return cache;
});
useServerInsertedHTML(() => {
return (
<style
data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
dangerouslySetInnerHTML={{
__html: Object.values(cache.inserted).join(' '),
}}
/>
);
});
return <CacheProvider value={cache}>{children}</CacheProvider>;
}app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import EmotionRegistry from '@/lib/registry';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Simfinity Components - Next.js Example',
description: 'Example Next.js app using simfinity-fe-components',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<EmotionRegistry>
{children}
</EmotionRegistry>
</body>
</html>
);
}Make sure your next.config.js includes:
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['simfinity-fe-components'],
experimental: {
esmExternals: 'loose',
},
webpack: (config) => {
config.resolve.extensionAlias = {
'.js': ['.js', '.ts', '.tsx'],
};
return config;
},
};
module.exports = nextConfig;- Always use 'use client' for components that use Material-UI
- Separate providers into their own client components
- Wrap Material-UI components in proper providers
- Use Emotion Registry for better SSR support
- Configure Next.js to transpile your package
Solution: Make sure all Material-UI components are wrapped in client components
Solution: Ensure CssBaseline is included and Emotion Registry is set up
Solution: Use the Emotion Registry pattern for consistent server/client rendering
This setup ensures Material-UI works properly with Next.js App Router! 🎉