Use Theme
boldkit/use-themeFreeHook
Theme provider hook with light/dark/system support and localStorage persistence. Used by Sonner toast for theme-aware rendering.
Live preview
live · rendered by the registry
Rendered by boldkit on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://boldkit.dev/r/use-theme.jsonSource
1/* eslint-disable react-refresh/only-export-components */2import * as React from 'react'34type Theme = 'light' | 'dark' | 'system'56interface ThemeProviderProps {7 children: React.ReactNode8 defaultTheme?: Theme9 storageKey?: string10}1112interface ThemeProviderState {13 theme: Theme14 setTheme: (theme: Theme) => void15 resolvedTheme: 'light' | 'dark'16}1718const ThemeProviderContext = React.createContext<ThemeProviderState | undefined>(19 undefined20)2122export function ThemeProvider({23 children,24 defaultTheme = 'system',25 storageKey = 'boldkit-theme',26}: ThemeProviderProps) {27 const [theme, setTheme] = React.useState<Theme>(() => {28 if (typeof window !== 'undefined') {29 return (localStorage.getItem(storageKey) as Theme) || defaultTheme30 }31 return defaultTheme32 })3334 const resolvedTheme = React.useMemo<'light' | 'dark'>(() => {35 if (typeof window === 'undefined') return 'light'36 if (theme === 'dark') return 'dark'37 if (theme === 'light') return 'light'38 return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'39 }, [theme])4041 React.useEffect(() => {42 const root = window.document.documentElement43 // Idempotent: during a circular-reveal theme toggle the class is flipped44 // imperatively inside the View Transition callback (see lib/theme-transition).45 // Re-running remove('light','dark') + add() here would momentarily leave the46 // element with no theme class — a one-frame flash that races the transition47 // and reads as "switch first, then animate". Skip when already correct.48 if (root.classList.contains(resolvedTheme)) return49 root.classList.remove('light', 'dark')50 root.classList.add(resolvedTheme)51 }, [resolvedTheme])5253 const value = React.useMemo(54 () => ({55 theme,56 setTheme: (newTheme: Theme) => {57 localStorage.setItem(storageKey, newTheme)58 setTheme(newTheme)59 },60 resolvedTheme,61 }),62 [theme, resolvedTheme, storageKey]63 )6465 return (66 <ThemeProviderContext.Provider value={value}>67 {children}68 </ThemeProviderContext.Provider>69 )70}7172export function useTheme() {73 const context = React.useContext(ThemeProviderContext)7475 if (context === undefined) {76 throw new Error('useTheme must be used within a ThemeProvider')77 }7879 return context80}
