Add Persistent Dark Mode to React with Chakra UI
Enable system-aware dark mode and user-toggle persistence in React using Chakra UI's color mode system.
01 Oct 2025, 00:50 UTC

Problem and takeaway
You want a React interface that respects the visitor's operating-system theme and remembers their manual choice. Writing custom CSS media queries and managing theme state yourself often produces mismatched component colors and a flash of the wrong theme on reload. Chakra UI's color mode system handles system-preference detection, theme switching, and persistence through its provider and hook.
Desired outcome
- The page starts in the visitor's system-preferred light or dark mode.
- A toggle switches between modes and updates Chakra UI components automatically.
- The chosen mode survives a full page reload.
Prerequisites
- React 16.8 or later, because
useColorModeis a React hook. - Chakra UI v2 or later, which applies the
color-schemeCSS property automatically. Chakra UI v1 requires a separateColorModeScriptstep. - Access to run
npm installin the project root and write topackage.json.
Procedure
1. Install Chakra UI and Emotion dependencies
Run this in the project root. It requires write permission to package.json and node_modules. A risk is peer-dependency conflict if your React version is older than expected.
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
2. Wrap the application in ChakraProvider
ChakraProvider supplies theme context and enables the color mode system. Add it at the top of your application tree, typically in your entry file:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ChakraProvider } from '@chakra-ui/react';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<ChakraProvider>
<App />
</ChakraProvider>
</React.StrictMode>
);
In JSX you would write the angle brackets directly; the example above escapes them for display.
3. Add the color-mode script before paint
Without an early script, the browser can paint light mode before JavaScript reads the stored preference, causing a visible flash. Render Chakra UI's color-mode script before ChakraProvider, or place it in the document head so it sets data-color-scheme on the <html> element early. Consult your installed Chakra UI version for the exact component or script tag.
4. Add a theme toggle component
useColorMode returns the current mode and a toggle function. This component switches modes and updates its label:
import { useColorMode, Button } from '@chakra-ui/react';
export function ThemeToggle() {
const [colorMode, toggleColorMode] = useColorMode();
return (
<Button
onClick={toggleColorMode}
aria-label='Toggle dark mode'>
Switch to {colorMode === 'light' ? 'dark' : 'light'} mode
</Button>
);
}
colorMode is either 'light' or 'dark'. toggleColorMode switches between them and Chakra UI updates component colors accordingly.
5. Confirm persistence across reloads
- Open browser DevTools and go to Application > Local Storage.
- Click the toggle and observe the stored value change.
- Reload the page.
- Confirm the page opens in the same mode you selected.
Expected checks
| Check | Expected result |
|---|---|
Inspect <html> | data-color-scheme is light or dark and changes when toggled. |
| Click toggle | Chakra UI components switch colors and the attribute updates. |
| Full reload | The previously chosen mode persists. |
| Change OS theme | If no manual choice is stored, the page follows the system preference. |
Limitations and recovery
- React versions earlier than 16.8 cannot run hooks, so this feature is unavailable.
- Custom CSS variables can override Chakra theme colors and cause inconsistent appearance.
- Missing the early color-mode script can cause a flash of the wrong theme on first paint.
- Rollback: remove
ChakraProvider, the color-mode script, and the toggle component. Uninstall the packages if you no longer need them. Because this change only affects frontend state, there is no database or migration to reverse.
To verify the final result, open the browser console, inspect the <html> element, and confirm data-color-scheme updates between light and dark when you toggle.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.