useDarkMode
dark mode with auto data persistence.
⚠️ Breaking Change in v6.1.0
Storage format has changed from boolean to string values. If you're upgrading from a previous version:
- Old format:
true/falseboolean values - New format:
"dark"/"light"string values - Auto-migration: Existing data will be automatically migrated
- SSR scripts: Need to be updated to handle string comparisons
Details
Migration Guide
If you have custom SSR scripts, update them as shown in the examples below. The library handles data migration automatically, but your custom scripts need to handle both old and new formats during the transition period.
Tips
click to open
For server-side rendered applications, since it's not possible to obtain the user's color preference on the server side, there might be a flicker during the first render. To avoid this issue, you can refer to the following steps.
- add a script before your content.
<script
dangerouslySetInnerHTML={{
// add a self-executing function
__html: `
(function () {
function setDark(dark) {
dark && document.documentElement.classList.add('dark');
}
let store = localStorage.getItem('reactuses-color-scheme');
let dark;
if(store === null){
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
dark = darkQuery.matches;
}else {
// Handle backward compatibility: convert old boolean values to string
if (store === 'true' || store === 'false') {
const boolValue = store === 'true';
const stringValue = boolValue ? 'dark' : 'light';
localStorage.setItem('reactuses-color-scheme', stringValue);
dark = boolValue;
} else {
// Handle string value ('dark' or 'light') directly
dark = store === 'dark';
}
}
setDark(dark)
})();
`,
}}
></script>
- To conveniently manage theme colors in a unified way, we recommend using context to store them.
import { useDarkMode } from "@reactuses/core";
import React, { createContext, useContext } from "react";
type ThemeContext = { theme: boolean; toggleTheme: () => void };
const ThemeContext = createContext<ThemeContext | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [dark, toggle] = useDarkMode({
classNameDark: "dark",
classNameLight: "light",
defaultValue: false,
});
return (
<ThemeContext.Provider value={{ theme: !!dark, toggleTheme: toggle }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
Usage
Live Editor
function Demo() { const [theme, toggleDark] = useDarkMode({ classNameDark: "dark", classNameLight: "light", defaultValue: false, }); return ( <div> <div>theme: {theme ? "dark" : "light"}</div> <br /> <div> <button onClick={toggleDark}>toggleDark</button> </div> </div> ); };
Result
Loading...
API
UseDarkOptions
| Property | Description | Type | DefaultValue |
|---|---|---|---|
| selector | CSS Selector for the target element applying to | string | 'html' |
| attribute | HTML attribute applying the target element | string | 'class' |
| defaultValue | default value | boolean | false |
| storageKey | Key to persist the data into localStorage/sessionStorage. | string | 'reactuses-color-scheme' |
| storage | Storage object, can be localStorage or sessionStorage | () => Storage | localStorage |
| classNameDark | name dark apply to element | string (Required) | - |
| classNameLight | name light apply to element | string (Required) | - |
useDarkMode
Returns
readonly [boolean | null, () => void, React.Dispatch<React.SetStateAction<boolean | null>>]: A tuple with the following elements:
- The current value of the dark state.
- A function to toggle the dark state.
- A function to update the dark state.
Arguments
| Argument | Description | Type | DefaultValue |
|---|---|---|---|
| options | - | UseDarkOptions (Required) | - |