use-dark-mode.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. "use client"
  2. import { useLocalStorage, useMedia } from "hooks";
  3. import React from "react";
  4. const useDarkMode = () => {
  5. // Use our useLocalStorage hook to persist state through a page refresh.
  6. // Read the recipe for this hook to learn more: usehooks.com/useLocalStorage
  7. const [enabledState, setEnabledState] = useLocalStorage<boolean>(
  8. "dark-mode-enabled",
  9. false
  10. );
  11. // See if user has set a browser or OS preference for dark mode.
  12. // The usePrefersDarkMode hook composes a useMedia hook (see code below).
  13. const prefersDarkMode = usePrefersDarkMode();
  14. // If enabledState is defined use it, otherwise fallback to prefersDarkMode.
  15. // This allows user to override OS level setting on our website.
  16. const enabled = enabledState ?? prefersDarkMode;
  17. // Fire off effect that add/removes dark mode class
  18. React.useEffect(
  19. () => {
  20. const className = "dark-mode";
  21. const element = window.document.body;
  22. if (enabled) {
  23. element.classList.add(className);
  24. } else {
  25. element.classList.remove(className);
  26. }
  27. },
  28. [enabled] // Only re-call effect when value changes
  29. );
  30. // Return enabled state and setter
  31. return [enabled, setEnabledState];
  32. }
  33. // Compose our useMedia hook to detect dark mode preference.
  34. // The API for useMedia looks a bit weird, but that's because ...
  35. // ... it was designed to support multiple media queries and return values.
  36. // Thanks to hook composition we can hide away that extra complexity!
  37. // Read the recipe for useMedia to learn more: usehooks.com/useMedia
  38. function usePrefersDarkMode() {
  39. return useMedia<boolean>(["(prefers-color-scheme: dark)"], [true], false);
  40. }
  41. export default useDarkMode