typescriptintermediate
useDarkMode Hook
Dark mode hook that syncs with system preferences and persists user choice to localStorage.
typescriptPress ⌘/Ctrl + Shift + C to copy
import { useState, useEffect } from 'react';
export function useDarkMode() {
const [isDark, setIsDark] = useState(() => {
if (typeof window === 'undefined') return false;
const stored = localStorage.getItem('dark-mode');
if (stored !== null) return stored === 'true';
return window.matchMedia('(prefers-color-scheme: dark)').matches;
});
useEffect(() => {
const root = document.documentElement;
if (isDark) {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
localStorage.setItem('dark-mode', String(isDark));
}, [isDark]);
useEffect(() => {
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e: MediaQueryListEvent) => {
if (localStorage.getItem('dark-mode') === null) {
setIsDark(e.matches);
}
};
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, []);
return { isDark, toggle: () => setIsDark(p => !p), setIsDark };
}Use Cases
- Theme switching
- Respecting user preferences
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
useFetch — Generic Data Fetching Hook
Custom React hook for data fetching with loading, error, and refetch states using the Fetch API.
Best for: API data fetching
#hooks#fetch
typescriptbeginner
useDebounce — Debounced Value Hook
Debounce any rapidly-changing value with a configurable delay. Useful for search inputs and resize handlers.
Best for: Search-as-you-type
#hooks#debounce
typescriptintermediate
useLocalStorage — Persistent State Hook
Sync React state with localStorage including SSR safety, JSON serialization, and cross-tab updates.
Best for: User preferences
#hooks#localstorage
typescriptbeginner
useClickOutside — Outside Click Detection
Detect clicks outside a referenced element to close dropdowns, modals, and popover menus.
Best for: Dropdown menus
#hooks#click-outside