
MUI
Overview
MUI (Material UI) is a React component library that implements Google's Material Design. It ships with a comprehensive set of pre-built components and a powerful theming system that accepts a fully typed theme object — covering colors, typography, spacing, shape, breakpoints, and component-level overrides — through its createTheme API.
Project Files
| File | Purpose |
|---|---|
muiTheme.ts | createTheme() with full light and dark palettes |
MuiThemeProvider.tsx | Applies the MUI theme via ThemeProvider |
MuiThemeSyncer.tsx | Keeps MUI's color scheme in sync with next-themes for light / dark mode |
Configuration
Theme
MUI reads from tokens.ts. Both light and dark palettes are fully populated using the colorSchemes API introduced in MUI v7:
import { tokens } from "@/styles/_generated/tokens";
export const muiTheme = createTheme({
colorSchemes: {
light: {
palette: {
primary: { main: tokens.light.colorPrimary },
secondary: { main: tokens.light.colorSecondary },
background: { default: tokens.light.colorBackground },
},
},
dark: {
palette: {
primary: { main: tokens.dark.colorPrimary },
secondary: { main: tokens.dark.colorSecondary },
background: { default: tokens.dark.colorBackground },
},
},
},
});Overrides
muiComponentOverrides.ts applies default props globally to every MUI component instance via the components key in createTheme:
export const muiTheme = createTheme({
components: {
MuiButtonBase: {
defaultProps: {
LinkComponent: Link, // swaps MUI's default `<a>` with Next.js `<Link>` for client-side navigation
},
},
MuiMenu: {
defaultProps: {
disableScrollLock: true, // prevents layout shift from the scrollbar being hidden
},
},
},
});Light & Dark Mode
dark Class
MUI's light and dark palette values are populated via this project's codegen pipeline.
colorSchemeSelector: "class" tells MUI to activate its dark palette when dark
is on <html>, which is what next-themes toggles.
export const muiTheme = createTheme({
colorSchemeSelector: "class"
});Syncers
While the dark class is enough for light / dark mode to work visually, MUI keeps
its own internal state of which color scheme it is in.
When next-themes toggles the dark class, MUI does not detect this.
For any extra MUI implementation & client-side functionality that needs to pull data
from MUI's state, a Syncer is needed to keep it up to date with the resolved theme
from next-themes.
MuiThemeSyncer calls setMode() whenever resolvedTheme changes to keep MUI's
internal state in sync.
export const MuiThemeSyncer = () => {
const { resolvedTheme } = useTheme();
const { setMode } = useColorScheme();
useEffect(() => {
setMode(resolvedTheme === "dark" ? "dark" : "light");
}, [resolvedTheme, setMode]);
return null;
};This Syncer is placed inside the MuiThemeProvider so all components have access to useColorScheme():
type MuiThemeProviderProps = {
children: ReactNode;
};
export const MuiThemeProvider = ({ children }: MuiThemeProviderProps) => {
return (
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<ThemeProvider theme={muiTheme}>
<MuiThemeSyncer />
{children}
</ThemeProvider>
</AppRouterCacheProvider>
);
};Injection Script
To avoid a flash on first load, MUI's InitMuiColorSchemeScript is placed in layout.tsx and run before serving the page.
// layout.tsx
<InitMuiColorSchemeScript attribute="class" defaultMode="system" />See Pain Points: SSR Flicker for details.
Components
All MUI components work exactly as documented. The project theme is applied automatically via MuiThemeProvider, so every component inherits the correct colors, typography, and shape without any extra setup.
