useOmarchyTheme

hook

Reads and writes the active Omarchy theme, mirroring it onto the document element and localStorage.

bunx --bun shadcn@latest add yamz8/omarchy-shadcn-registry/use-omarchy-theme

Dependencies

registry
yamz8/omarchy-shadcn-registry/omarchy-palette

Source

registry/hooks/use-omarchy-theme.ts
"use client"

import * as React from "react"

import {
  DEFAULT_OMARCHY_THEME,
  getOmarchyCssVariables,
  getOmarchyTheme,
  isOmarchyThemeName,
  type OmarchyThemeName,
} from "@/lib/omarchy-palette"

const STORAGE_KEY = "omarchy-theme"

const listeners = new Set<() => void>()
let currentTheme: OmarchyThemeName = DEFAULT_OMARCHY_THEME

function subscribe(onStoreChange: () => void) {
  listeners.add(onStoreChange)
  window.addEventListener("storage", onStoreChange)

  return () => {
    listeners.delete(onStoreChange)
    window.removeEventListener("storage", onStoreChange)
  }
}

function getSnapshot(): OmarchyThemeName {
  try {
    const stored = window.localStorage.getItem(STORAGE_KEY)

    if (stored && isOmarchyThemeName(stored)) {
      currentTheme = stored
    }
  } catch {
    // Storage can be unavailable (private mode, blocked site data).
  }

  return currentTheme
}

function getServerSnapshot(): OmarchyThemeName {
  return DEFAULT_OMARCHY_THEME
}

export function applyOmarchyTheme(
  name: OmarchyThemeName,
  root: HTMLElement = document.documentElement
) {
  const theme = getOmarchyTheme(name)

  if (!theme) {
    return
  }

  root.dataset.omarchyTheme = theme.name
  root.style.colorScheme = theme.mode

  for (const [property, value] of Object.entries(
    getOmarchyCssVariables(theme)
  )) {
    root.style.setProperty(property, value)
  }
}

/**
 * Reads and writes the active Omarchy theme, mirroring it onto
 * `document.documentElement[data-omarchy-theme]` and localStorage.
 */
export function useOmarchyTheme() {
  const theme = React.useSyncExternalStore(
    subscribe,
    getSnapshot,
    getServerSnapshot
  )

  React.useEffect(() => {
    applyOmarchyTheme(theme)
  }, [theme])

  const setTheme = React.useCallback((next: OmarchyThemeName) => {
    currentTheme = next

    try {
      window.localStorage.setItem(STORAGE_KEY, next)
    } catch {
      // Ignore write failures and keep the in-memory selection.
    }

    applyOmarchyTheme(next)

    for (const listener of listeners) {
      listener()
    }
  }, [])

  return { theme, setTheme }
}