themeProvider.tsx 1.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. 'use client';
  2. import { createContext, useContext, useEffect, useState, useCallback } from 'react';
  3. type Theme = 'light' | 'dark';
  4. const ThemeContext = createContext<{
  5. theme: Theme;
  6. toggleTheme: () => void;
  7. }>({
  8. theme: 'light',
  9. toggleTheme: () => {},
  10. });
  11. export function useThemeContext() {
  12. return useContext(ThemeContext);
  13. }
  14. export function ThemeProvider({ children }: { children: React.ReactNode }) {
  15. const [theme, setTheme] = useState<Theme>('light');
  16. const [mounted, setMounted] = useState(false);
  17. useEffect(() => {
  18. const stored = localStorage.getItem('bitforum-theme') as Theme | null;
  19. const initial = stored ?? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  20. setTheme(initial);
  21. document.documentElement.classList.toggle('dark', initial === 'dark');
  22. setMounted(true);
  23. }, []);
  24. const toggleTheme = useCallback(() => {
  25. setTheme((prev) => {
  26. const next = prev === 'light' ? 'dark' : 'light';
  27. localStorage.setItem('bitforum-theme', next);
  28. document.documentElement.classList.toggle('dark', next === 'dark');
  29. return next;
  30. });
  31. }, []);
  32. if (!mounted) {
  33. return <>{children}</>;
  34. }
  35. return (
  36. <ThemeContext.Provider value={{ theme, toggleTheme }}>
  37. {children}
  38. </ThemeContext.Provider>
  39. );
  40. }