Command Palette

Search for a command to run...
HQ UI

Vite

Learn how to integrate React Aria in Vite

Create Project

Run the init command to create a new Vite React project or to setup an existing one:

npx shadcn@latest init https://hq-ui.vercel.app/r/default

or you can choose any theme listed here

Add components

You can now start adding components to your project.

npx shadcn@latest add https://hq-ui.vercel.app/r/button

Dark Mode

Theme Provider

theme-provider.tsx
import { createContext, use, useEffect, useState } from "react"

type Theme = "dark" | "light" | "system"

type ThemeProviderProps = {
  children: React.ReactNode
  defaultTheme?: Theme
  storageKey?: string
}

type ThemeProviderState = {
  theme: Theme
  setTheme: (theme: Theme) => void
}

const initialState: ThemeProviderState = {
  theme: "system",
  setTheme: () => null,
}

const ThemeProviderContext = createContext<ThemeProviderState>(initialState)

function ThemeProvider({
  children,
  defaultTheme = "system",
  storageKey = "theme",
  ...props
}: ThemeProviderProps) {
  const [theme, setTheme] = useState<Theme>(() => {
    if (typeof window !== "undefined") {
      return (localStorage?.getItem(storageKey) as Theme) || defaultTheme
    }
    return defaultTheme
  })

  useEffect(() => {
    if (typeof window !== "undefined") {
      const root = window.document.documentElement

      root.classList.remove("light", "dark")

      if (theme === "system") {
        const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
          ? "dark"
          : "light"
        root.classList.add(systemTheme)
        return
      }

      root.classList.add(theme)
    }
  }, [theme])

  const value = {
    theme,
    setTheme: (theme: Theme) => {
      if (typeof window !== "undefined") {
        localStorage.setItem(storageKey, theme)
      }
      setTheme(theme)
    },
  }

  return (
    <ThemeProviderContext {...props} value={value}>
      {children}
    </ThemeProviderContext>
  )
}

const useTheme = () => {
  const context = use(ThemeProviderContext)
  if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider")
  return context
}

export { ThemeProvider, useTheme, type Theme, type ThemeProviderProps, type ThemeProviderState }

Then use in your root layout

App.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { ThemeProvider } from './components/theme-provider.tsx'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <ThemeProvider>
      <App />
    </ThemeProvider>
  </React.StrictMode>,
)

Theme Switcher

npm i @tabler/icons-react
components/theme-switcher.tsx
'use client'

import { useTheme } from '@/components/theme-provider'
import { IconMoon, IconSun } from '@tabler/icons-react'
import { Button } from '@/components/ui/button'

export function ThemeSwitcher({ variant = 'outline' }: { variant?: 'outline' | 'ghost' }) {
  const { resolvedTheme, setTheme } = useTheme()

  return (
    <Button
      aria-label={`Switch to ${resolvedTheme}` === 'light' ? 'dark' : 'light' + 'mode'}
      onPress={() => setTheme(resolvedTheme === 'light' ? 'dark' : 'light')}
      size='icon'
      variant={variant}
    >
      <IconSun aria-hidden className='rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0' />
      <IconMoon aria-hidden className='absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100' />
    </Button>
  )
}