Skip to main content

Overview

The useTheme hook provides access to the current theme and a function to update it. The theme is persisted to localStorage and automatically applied to the document root element.

Import

import { useTheme } from '@app/providers/Theme'

Type Signature

function useTheme(): {
  theme: Theme
  setTheme: (t: Theme) => void
}

type Theme = "white" | "dark"

Return Value

theme
'white' | 'dark'
The current active theme. Defaults to 'dark' if no theme is stored in localStorage.
setTheme
(t: Theme) => void
Function to update the theme. Accepts either 'white' or 'dark' as the new theme value.When called, this function:
  • Updates the theme state
  • Persists the new theme to localStorage
  • Sets the data-theme attribute on the document root element

Usage Example

Basic Theme Toggle

import { useTheme } from '@app/providers/Theme'

function ThemeToggle() {
  const { theme, setTheme } = useTheme()

  const toggleTheme = () => {
    setTheme(theme === 'dark' ? 'white' : 'dark')
  }

  return (
    <button onClick={toggleTheme}>
      Switch to {theme === 'dark' ? 'Light' : 'Dark'} Mode
    </button>
  )
}

Theme Selector Component

import { useTheme } from '@app/providers/Theme'

function ThemeSelector() {
  const { theme, setTheme } = useTheme()

  return (
    <div className="theme-selector">
      <button
        onClick={() => setTheme('white')}
        className={theme === 'white' ? 'active' : ''}
      >
        Light
      </button>
      <button
        onClick={() => setTheme('dark')}
        className={theme === 'dark' ? 'active' : ''}
      >
        Dark
      </button>
    </div>
  )
}

Conditional Rendering Based on Theme

import { useTheme } from '@app/providers/Theme'

function ThemedIcon() {
  const { theme } = useTheme()

  return (
    <img
      src={theme === 'dark' ? '/icons/logo-dark.svg' : '/icons/logo-light.svg'}
      alt="Logo"
    />
  )
}

Setup Required

The useTheme hook must be used within a ThemeProvider. Wrap your application root with the provider:
import { ThemeProvider } from '@app/providers/Theme'

function App() {
  return (
    <ThemeProvider>
      <YourApp />
    </ThemeProvider>
  )
}

How It Works

  1. Initialization: On mount, the provider checks localStorage for a saved theme preference. If none exists, defaults to 'dark'
  2. Persistence: When setTheme is called:
    • The new theme value is saved to localStorage under the key "theme"
    • The data-theme attribute is set on document.documentElement
    • The state updates, triggering a re-render
  3. CSS Integration: The theme is applied via the data-theme attribute, allowing CSS to respond:
/* Dark theme styles */
[data-theme="dark"] {
  --background-color: #1a1a1a;
  --text-color: #ffffff;
}

/* Light theme styles */
[data-theme="white"] {
  --background-color: #ffffff;
  --text-color: #000000;
}

Error Handling

If useTheme is called outside of a ThemeProvider, it throws an error:
Error: useTheme must be used inside ThemeProvider
Always ensure your component tree includes the provider at a parent level.

Storage Key

The theme preference is stored in localStorage with the key: "theme"

Source

Implemented in: src/app/providers/Theme.tsx:25

Build docs developers (and LLMs) love