Color Mode
Chakra UI comes with built-in support for managing color mode in your apps.
By default, most of Chakra's components are dark mode compatible. In some scenarios, you might need to make your component respond to color mode.
Tip: Chakra stores the color mode in
localStorage
and appends a className to thebody
to ensure the color mode is persistent.
Setup
To get dark mode working correctly, you need to do two things:
Update your theme config to determine how Chakra should manage color mode updates.
Add the
ColorModeScript
to your application, and set the initial color mode your application should start with to eitherlight
,dark
orsystem
. It islight
by default.
Note: When using
system
as initial color mode, the theme will change with the system preference. However, if another theme is manually selected by the user then that theme will be used on the next page load. To reset it to system preference, simply remove thechakra-ui-color-mode
entry from localStorage.
Updating the theme config
The theme config for color mode has 2 options:
initialColorMode
: The initial mode you'd like your app to start withuseSystemColorMode
: Iftrue
, your app will change color mode based on the user's system preferences.
// theme.js
// 1. import `extendTheme` function
import { extendTheme } from "@chakra-ui/react";
// 2. Add your color mode config
const config = {
initialColorMode: "light",
useSystemColorMode: false,
};
// 3. extend the theme
const theme = extendTheme({ config });
export default theme;
For typescript, you need to explicitly describe the theme config type as
ThemeConfig
object.
// theme.ts
// 1. import `extendTheme` function
import { extendTheme, type ThemeConfig } from "@chakra-ui/react";
// 2. Add your color mode config
const config: ThemeConfig = {
initialColorMode: "light",
useSystemColorMode: false,
};
// 3. extend the theme
const theme = extendTheme({ config });
export default theme;
Remember to pass your custom
theme
to theChakraProvider
, otherwise your color mode config won't be taken into consideration.
Behavior of ColorMode
The current hierarchy of how the color mode is defined is as follows:
if
useSystemColorMode
is truesystem
-color will be used as default -initialColorMode
is the fallback if system color mode can't be resolvedif
data-theme
prop is defined through e.g.ColorModeScript
/ after modification/initial load of the colorMode this value will be usedif
colorModeManager
=localStorage
and a value is defined forchakra-ui-color-mode
this will be usedif
initialColorMode
=system
system-color will be used as default -initialColorMode
is the fallback if system color mode isn't resolvedif
initialColorMode
='light'|'dark'
the corresponding value will be used
We currently accept 3 different values for initialColorMode
:
light
,dark
,system
Difference between initialColorMode='system'
and useSystemColorMode
if useSystemColorMode=true
we will always try to match the users
system
-color and fallback to initialColorMode
. With this behavior, the
colorMode toggle won't have any effect.
if initialColorMode='system'
we will as well always try to match the users
system
-color and fallback to light
. After the user has toggled the value,
this value will be used.
Adding the ColorModeScript
The color mode script needs to be added before the content inside the body
tag
for local storage syncing to work correctly.
When setting the initial color mode, we recommend adding it as a config to your theme and reference that in the
ColorModeScript
.
To use
ColorModeScript
on a site with strictContent-Security-Policy
, you can use thenonce
prop that will be passed to the<script>
tag.
For Next.js
For Next.js, you need to add the ColorModeScript
to the _document.js
file.
// pages/_document.js
import { ColorModeScript } from "@chakra-ui/react";
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
import theme from "./theme";
export default class Document extends NextDocument {
render() {
return (
<Html lang="en">
<Head />
<body>
{/* 👇 Here's the script */}
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<Main />
<NextScript />
</body>
</Html>
);
}
}
For Create React App
For Create React App, you need to add the ColorModeScript
to the index.js
file.
// index.js
import { ColorModeScript } from "@chakra-ui/react";
import * as ReactDOM from "react-dom/client";
import App from "./App";
import theme from "./theme";
const rootElement = document.getElementById("root");
ReactDOM.createRoot(rootElement).render(
<>
{/* 👇 Here's the script */}
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<App />
</>
);
For Gatsby
Install @chakra-ui/gatsby-plugin
in your project. You can read more in the
Chakra UI + Gatsby guide.
Changing Color Mode
To manage color mode in your application, chakra exposes the useColorMode
or
useColorModeValue
hooks.
useColorMode
useColorMode
is a React hook that gives you access to the current color mode,
and a function to toggle the color mode.
function Example() {
const { colorMode, toggleColorMode } = useColorMode();
return (
<header>
<Button onClick={toggleColorMode}>
Toggle {colorMode === "light" ? "Dark" : "Light"}
</Button>
</header>
);
}
Calling toggleColorMode
anywhere in your app tree toggles the color mode from
light
or dark
and vice versa.
useColorModeValue
useColorModeValue
is a React hook used to change any value or style based on
the color mode. It takes 2 arguments: the value in light mode, and the value in
dark mode.
// Here's the signature
const value = useColorModeValue(lightModeValue, darkModeValue);
Here's an example that changes the background-color
and color
using the
useColorModeValue
hook.
Click the Toggle Mode button to see it in action.
function StyleColorMode() {
const { toggleColorMode } = useColorMode();
const bg = useColorModeValue("red.500", "red.200");
const color = useColorModeValue("white", "gray.800");
return (
<>
<Box mb={4} bg={bg} color={color}>
This box's style will change based on the color mode.
</Box>
<Button size="sm" onClick={toggleColorMode}>
Toggle Mode
</Button>
</>
);
}
Forcing a specific mode
In some occasions, you might want Chakra components to look the same in both
light and dark modes. To achieve this, wrap the component in a LightMode
or
DarkMode
component. Doing this will override the global colorMode
.
Click the "Toggle Mode" button to see it in action.
function Example() {
const { colorMode, toggleColorMode } = useColorMode();
return (
<HStack>
<LightMode>
<Button size="sm" colorScheme="blue">
Light Mode Always
</Button>
</LightMode>
<DarkMode>
<Button size="sm" colorScheme="blue">
Dark Mode Always
</Button>
</DarkMode>
<Button size="sm" colorScheme="blue" onClick={toggleColorMode}>
Toggle Mode
</Button>
</HStack>
);
}
Add colorModeManager (Optional, for SSR)
For server-side rendered sites, e.g. in Next.js, you may want to know the color
preference of a user upfront so you can avoid rendering the initial color mode
and then changing it during hydration (so-called flashing
).
If you don't use SSR or don't care about this, you don't need to pass anything.
Chakra will use localStorageManager
by default.
Here's how to do this in Next.js 9.3 or newer:
- Create a reusable wrapper as demonstrated in the examples:
// e.g. src/Chakra.js
// a) import `ChakraProvider` component as well as the storageManagers
import {
ChakraProvider,
cookieStorageManagerSSR,
localStorageManager,
} from "@chakra-ui/react";
export function Chakra({ cookies, children }) {
// b) Pass `colorModeManager` prop
const colorModeManager =
typeof cookies === "string"
? cookieStorageManagerSSR(cookies)
: localStorageManager;
return (
<ChakraProvider colorModeManager={colorModeManager}>
{children}
</ChakraProvider>
);
}
// also export a reusable function getServerSideProps
export function getServerSideProps({ req }) {
return {
props: {
// first time users will not have any cookies and you may not return
// undefined here, hence ?? is necessary
cookies: req.headers.cookie ?? "",
},
};
}
- Import your wrapper component setting up Chakra:
// setup your wrapper in the _app file (e.g: pages/_app.js)
import { Chakra } from "../src/Chakra";
export default function App({ Component, pageProps }) {
return (
<Chakra cookies={pageProps.cookies}>
<Component {...pageProps} />
</Chakra>
);
}
// e.g pages/index.js
export default function Index() {
return <h1>Example</h1>;
}
// re-export the reusable `getServerSideProps` function
export { getServerSideProps } from "./Chakra";
If you need to know the name of the Chakra cookie for specific reasons, it's
chakra-ui-color-mode
. Also, if you usecolorModeManager
, you can avoid adding the<ColorModeScript />
to_document.js
.
Important: if you're using
Next.js 9.3
or newer, the Next.js team recommends avoidinggetInitialProps
. The following example is for Next 9.2 or older!
// pages/_app.js
import {
ChakraProvider,
cookieStorageManagerSSR,
localStorageManager,
} from "@chakra-ui/react";
export default function App({ cookies }) {
// 2. Pass `colorModeManager` prop - it finds the relevant cookie on its own
return (
<ChakraProvider
colorModeManager={
typeof cookies === "string"
? cookieStorageManagerSSR(cookies)
: localStorageManager
}
>
<h1>Example</h1>
</ChakraProvider>
);
}
App.getInitialProps = ({ req }) => {
return {
// first time users will not have any cookies and you may not return
// undefined here, hence ?? is necessary
cookies: req.headers.cookie ?? "",
};
};
Color Mode Flash Issue
In some cases, when you switch to dark mode and refresh the page, you might experience a quick flash of light mode before it switches correctly.
This is a known issue and we're looking to fix it. If you have some ideas, feel free to share with us on Discord or Github.