It uses internally useLocalStorage() to persist the value and listens the OS color scheme preferences.
Note: If you use this hook in an SSR context, set the initializeWithValue option to false.
Usage
import { useDarkMode } from './useDarkMode'
export default function Component() {
const { isDarkMode, toggle, enable, disable } = useDarkMode()
return (
<div>
<p>Current theme: {isDarkMode ? 'dark' : 'light'}</p>
<button onClick={toggle}>Toggle</button>
<button onClick={enable}>Enable</button>
<button onClick={disable}>Disable</button>
</div>
)
}
API
function useDarkMode(options?: DarkModeOptions): DarkModeReturn
Custom hook that returns the current state of the dark mode.
Parameters
| Name | Type | Default value | Description |
|---|---|---|---|
options? | DarkModeOptions | {} | The initial value of the dark mode, default false. |
Returns
An object containing the dark mode's state and its controllers.
Type declaration
DarkModeOptions
The hook options.
| Name | Type | Description |
|---|---|---|
defaultValue | boolean | The initial value of the dark mode. |
initializeWithValue | boolean | If true (default), the hook will initialize reading localStorage. In SSR, you should set it to false, returning the defaultValue or false initially. |
localStorageKey | string | The key to use in the local storage. |
DarkModeReturn
The hook return type.
| Name | Type | Description |
|---|---|---|
disable | () => void | Function to disable the dark mode. |
enable | () => void | Function to enable the dark mode. |
isDarkMode | boolean | The current state of the dark mode. |
set | (value: boolean) => void | Function to set a specific value to the dark mode. |
toggle | () => void | Function to toggle the dark mode. |
Hook
import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect'
import { useLocalStorage } from '../useLocalStorage'
import { useMediaQuery } from '../useMediaQuery'
import { usePrevious } from '../usePrevious'
const COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)'
const LOCAL_STORAGE_KEY = 'usehooks-ts-dark-mode'
/** The hook options. */
export type DarkModeOptions = {
/**
* The initial value of the dark mode.
* @default false
*/
defaultValue?: boolean
/**
* The key to use in the local storage.
* @default 'usehooks-ts-dark-mode'
*/
localStorageKey?: string
/**
* If `true` (default), the hook will initialize reading `localStorage`.
* In SSR, you should set it to `false`, returning the `defaultValue` or `false` initially.
* @default true
*/
initializeWithValue?: boolean
}
/** The hook return type. */
export type DarkModeReturn = {
/** The current state of the dark mode. */
isDarkMode: boolean
/** Function to toggle the dark mode. */
toggle: () => void
/** Function to enable the dark mode. */
enable: () => void
/** Function to disable the dark mode. */
disable: () => void
/** Function to set a specific value to the dark mode. */
set: (value: boolean) => void
}
/**
* Custom hook that returns the current state of the dark mode.
* @param {?DarkModeOptions} [options] - The initial value of the dark mode, default `false`.
* @returns {DarkModeReturn} An object containing the dark mode's state and its controllers.
* @public
* @see [Documentation](https://usehooks-ts.com/react-hook/use-dark-mode)
* @example
* ```tsx
* const { isDarkMode, toggle, enable, disable, set } = useDarkMode({ defaultValue: true });
* ```
*/
export function useDarkMode(options: DarkModeOptions = {}): DarkModeReturn {
const { defaultValue, localStorageKey = LOCAL_STORAGE_KEY, initializeWithValue = true } = options
const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY, {
initializeWithValue,
defaultValue,
})
const [isDarkMode, setDarkMode] = useLocalStorage<boolean>(
localStorageKey,
defaultValue ?? isDarkOS ?? false,
{ initializeWithValue },
)
const prevIsDarkOS = usePrevious(isDarkOS)
// Only sync when OS preference actually changes, not on initial mount
useIsomorphicLayoutEffect(() => {
if (prevIsDarkOS !== undefined && isDarkOS !== prevIsDarkOS) {
setDarkMode(isDarkOS)
}
}, [isDarkOS, prevIsDarkOS, setDarkMode])
return {
isDarkMode,
toggle: () => {
setDarkMode(prev => !prev)
},
enable: () => {
setDarkMode(true)
},
disable: () => {
setDarkMode(false)
},
set: value => {
setDarkMode(value)
},
}
}