Dark mode
Material UI comes with two palette modes: light (the default) and dark.
Dark mode by default
You can make your application use the dark theme as the default—regardless of the user's preference—by adding mode: 'dark'
to the createTheme
helper:
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
const darkTheme = createTheme({
palette: {
mode: 'dark',
},
});
export default function App() {
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<main>This app is using the dark mode</main>
</ThemeProvider>
);
}
Adding mode: 'dark'
to the createTheme
helper modifies several palette values, as shown in the following demo:
Typography
palette.text.primary
#fff
palette.text.secondary
rgba(255, 255, 255, 0.7)
palette.text.disabled
rgba(255, 255, 255, 0.5)
Buttons
palette.action.active
#fff
palette.action.hover
rgba(255, 255, 255, 0.08)
palette.action.selected
rgba(255, 255, 255, 0.16)
palette.action.disabled
rgba(255, 255, 255, 0.3)
palette.action.disabledBackground
rgba(255, 255, 255, 0.12)
Background
palette.background.default
#121212
palette.background.paper
#121212
Divider
palette.divider
rgba(255, 255, 255, 0.12)
Typography
palette.text.primary
rgba(0, 0, 0, 0.87)
palette.text.secondary
rgba(0, 0, 0, 0.6)
palette.text.disabled
rgba(0, 0, 0, 0.38)
Buttons
palette.action.active
rgba(0, 0, 0, 0.54)
palette.action.hover
rgba(0, 0, 0, 0.04)
palette.action.selected
rgba(0, 0, 0, 0.08)
palette.action.disabled
rgba(0, 0, 0, 0.26)
palette.action.disabledBackground
rgba(0, 0, 0, 0.12)
Background
palette.background.default
#fff
palette.background.paper
#fff
Divider
palette.divider
rgba(0, 0, 0, 0.12)
Adding <CssBaseline />
inside of the <ThemeProvider>
component will also enable dark mode for the app's background.
Dark mode with a custom palette
To use custom palettes for light and dark modes, you can create a function that will return the correct palette depending on the selected mode, as shown here:
const getDesignTokens = (mode: PaletteMode) => ({
palette: {
mode,
...(mode === 'light'
? {
// palette values for light mode
primary: amber,
divider: amber[200],
text: {
primary: grey[900],
secondary: grey[800],
},
}
: {
// palette values for dark mode
primary: deepOrange,
divider: deepOrange[700],
background: {
default: deepOrange[900],
paper: deepOrange[900],
},
text: {
primary: '#fff',
secondary: grey[500],
},
}),
},
});
You can see on the example that there are different colors used based on whether the mode is light or dark. The next step is to use this function when creating the theme.
export default function App() {
const [mode, setMode] = React.useState<PaletteMode>('light');
const colorMode = React.useMemo(
() => ({
// The dark mode switch would invoke this method
toggleColorMode: () => {
setMode((prevMode: PaletteMode) =>
prevMode === 'light' ? 'dark' : 'light',
);
},
}),
[],
);
// Update the theme only if the mode changes
const theme = React.useMemo(() => createTheme(getDesignTokens(mode)), [mode]);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<Page />
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Here is a fully working example:
Toggling color mode
To give your users a way to toggle between modes, you can add React's context to a button's onClick
event, as shown in the following demo:
System preference
Users might have a preference for light or dark mode that they've set through their operating system—either systemwide, or for a single user agent.
You can make use of this preference with the useMediaQuery hook and the prefers-color-scheme media query.
The following demo shows how to enable dark mode automatically by checking for the user's preference in their OS or browser settings:
import * as React from 'react';
import useMediaQuery from '@mui/material/useMediaQuery';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
function App() {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode: prefersDarkMode ? 'dark' : 'light',
},
}),
[prefersDarkMode],
);
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Routes />
</ThemeProvider>
);
}
Styling in dark mode
Use the theme.applyStyles
utility to apply styles for a specific mode.
We recommend using this function over checking theme.palette.mode
to switch between styles as it has more benefits:
- It works with or without
CssVarsProvider
. The function automatically switches between overriding object styles or injecting pseudo-classes based on the upper provider. - It lets you prevent dark-mode SSR flickering when using with
CssVarsProvider
. - It can be used with Pigment CSS, our in-house zero-runtime CSS-in-JS solution.
- It is generally more readable and maintainable.
- It is slightly more performant as it doesn't require to do style recalculation but the bundle size of SSR generated styles is larger.
Usage
With the **styled**
function:
import { styled } from '@mui/material/styles';
const MyComponent = styled('div')(({ theme }) => ({
color: '#fff',
backgroundColor: theme.palette.primary.main,
...theme.applyStyles('dark', {
backgroundColor: theme.palette.secondary.main,
}),
'&:hover': {
boxShadow: theme.shadows[3],
backgroundColor: theme.palette.primary.dark,
...theme.applyStyles('dark', {
backgroundColor: theme.palette.secondary.dark,
}),
},
}));
With the **sx**
prop:
import Button from '@mui/material/Button';
<Button
sx={[
(theme) => ({
color: '#fff',
backgroundColor: theme.palette.primary.main,
...theme.applyStyles('dark', {
backgroundColor: theme.palette.secondary.main,
}),
'&:hover': {
boxShadow: theme.shadows[3],
backgroundColor: theme.palette.primary.dark,
...theme.applyStyles('dark', {
backgroundColor: theme.palette.secondary.dark,
}),
},
}),
]}
>
Submit
</Button>;
Codemod
We provide codemods to migrate your codebase from using theme.palette.mode
to use theme.applyStyles()
.
You can run each codemod below or all of them at once.
npx @mui/codemod@next v6.0.0/styled <path/to/folder-or-file>
npx @mui/codemod@next v6.0.0/sx-prop <path/to/folder-or-file>
npx @mui/codemod@next v6.0.0/theme-v6 <path/to/theme-file>
Run
v6.0.0/theme-v6
against the file that contains the customstyleOverrides
. Ignore this codemod if you don't have a custom theme.
API
theme.applyStyles(mode, styles) => CSSObject
Apply styles for a specific mode.
Arguments
mode
('light' | 'dark'
) - The mode for which the styles should be applied.styles
(CSSObject
) - An object which contains the styles to be applied for the specified mode.