npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

use-dark-switch

v1.0.1

Published

A powerful React Hook for implementing dark mode with enhanced features

Readme

✨ useDarkSwitch ✨

useDarkSwitch is a powerful React Hook designed to simplify implementing dark mode in your applications. It offers enhanced features such as customizable class names, sync with system preferences, and seamless state persistence using localStorage.

❤️ it? ⭐️ it on GitHub or Tweet about it.

Version Contributors Issues Forks

GitHub Repository | Report Issues

usedarkswitch


Features

  • Customizable: Configure class names for dark and light modes, or use a callback for advanced use cases.

  • Persistent State: Automatically saves user preferences in localStorage.

  • System Preference Support: Syncs with the user's system-wide dark mode setting.

  • Cross-tab Synchronization: Updates the theme across multiple tabs.

  • SSR Compatibility: Works seamlessly in server-side rendering environments.


Requirements

  • React Version: >=18.0.0

  • Peer Dependencies:

  • react

  • react-dom


Installation

Install useDarkSwitch via npm or yarn:


npm  install  use-dark-switch

or


yarn  add  use-dark-switch

Usage

Basic Example

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const App = () => {
  const { value, toggle, enable, disable } = useDarkSwitch();

  return (
    <div>
      <button onClick={disable}>☀ Light Mode</button>

      <button onClick={toggle}>Toggle</button>

      <button onClick={enable}>☾ Dark Mode</button>

      <p>Current mode: {value ? 'Dark' : 'Light'}</p>
    </div>
  );
};

export default App;

API

Hook Signature

const { value, enable, disable, toggle } = useDarkSwitch(initialValue, config);

Here's the updated content formatted as tables:


Parameters

| Parameter | Type | Default | Description | | ------------ | ------- | ------- | -------------------------------------------------------------------- | | initialValue | boolean | false | Initial state for dark mode (true for dark, false for light). | | config | object | {} | Configuration options for advanced use cases. See below for details. |


Configuration Options

| Key | Type | Default | Description | | -------------- | ----------- | ------------- | ------------------------------------------------------------------ | | classNameDark | string | dark-mode | CSS class for dark mode. | | classNameLight | string | light-mode | CSS class for light mode. | | element | HTMLElement | document.body | Element to which the class is applied. | | onChange | function | undefined | Callback triggered when the mode changes. Use for custom handling. | | storageKey | string | darkMode | Key used in localStorage to persist state. | | syncWithSystem | boolean | true | Whether to sync with the system's dark mode preference. |


Return Value

The hook returns an object with the following properties:

| Property | Type | Description | | -------- | -------- | ------------------------------------------------ | | value | boolean | Current mode (true for dark, false for light). | | enable | function | Function to enable dark mode. | | disable | function | Function to disable dark mode. | | toggle | function | Function to toggle between dark and light modes. |


Example: Advanced Usage

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const CustomDarkModeApp = () => {
  const darkMode = useDarkSwitch(false, {
    classNameDark: 'my-dark-mode',

    classNameLight: 'my-light-mode',

    element: document.getElementById('root'),

    onChange: (isDark) => {
      console.log(`Dark mode is now ${isDark ? 'enabled' : 'disabled'}`);
    },

    storageKey: 'customDarkMode',

    syncWithSystem: true,
  });

  return (
    <div>
      <button onClick={darkMode.disable}>Light</button>

      <button onClick={darkMode.toggle}>Toggle</button>

      <button onClick={darkMode.enable}>Dark</button>
    </div>
  );
};

export default CustomDarkModeApp;

More Examples

Beginner Usage

1. Basic Toggle with Icon

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

import { Moon, Sun } from 'lucide-react';

const BasicToggleWithIcon = () => {
  const { isDark, toggle } = useDarkSwitch();

  return (
    <button onClick={toggle} className="p-2 rounded-full bg-gray-200 dark:bg-gray-800">
      {isDark ? <Sun className="text-yellow-400" /> : <Moon className="text-gray-700" />}
    </button>
  );
};

2. Theme-Aware Text Component

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeAwareText = ({ children }) => {
  const { isDark } = useDarkSwitch();

  return <p className={`${isDark ? 'text-white' : 'text-black'}`}>{children}</p>;
};

// Usage

const App = () => (
  <div>
    <ThemeAwareText>This text adapts to the current theme!</ThemeAwareText>
  </div>
);

3. Simple Theme Switcher with Radio Buttons

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeSwitcher = () => {
  const { isDark, setMode } = useDarkSwitch();

  return (
    <div>
      <label>
        <input
          type="radio"
          name="theme"
          value="light"
          checked={!isDark}
          onChange={() => setMode(false)}
        />
        Light
      </label>

      <label>
        <input
          type="radio"
          name="theme"
          value="dark"
          checked={isDark}
          onChange={() => setMode(true)}
        />
        Dark
      </label>
    </div>
  );
};

Intermediate Usage

4. Theme Toggle with Transition

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const TransitionToggle = () => {
  const { isDark, toggle } = useDarkSwitch({
    transitionDuration: 300,
  });

  return (
    <div
      className={`p-4 transition-colors duration-300 ${
        isDark ? 'bg-gray-900 text-white' : 'bg-white text-black'
      }`}
    >
      <button
        onClick={toggle}
        className={`px-4 py-2 rounded transition-colors duration-300 ${
          isDark ? 'bg-white text-black' : 'bg-gray-900 text-white'
        }`}
      >
        Toggle Theme
      </button>

      <p className="mt-2">Current theme: {isDark ? 'Dark' : 'Light'}</p>
    </div>
  );
};

5. Theme-Aware Image Component

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeAwareImage = ({ lightSrc, darkSrc, alt }) => {
  const { isDark } = useDarkSwitch();

  return <img src={isDark ? darkSrc : lightSrc} alt={alt} className="max-w-full h-auto" />;
};

// Usage

const App = () => (
  <ThemeAwareImage
    lightSrc="/images/logo-light.png"
    darkSrc="/images/logo-dark.png"
    alt="Company Logo"
  />
);

6. Theme Persistence with Custom Storage Key

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const PersistentThemeToggle = () => {
  const { isDark, toggle } = useDarkSwitch({
    storageKey: 'my-app-theme-preference',
  });

  return (
    <div>
      <button onClick={toggle}>Switch to {isDark ? 'Light' : 'Dark'} Mode</button>

      <p>Your theme preference will be remembered even after you close the browser.</p>
    </div>
  );
};

Advanced Usage

7. Theme Provider with Context API

import React, { createContext, useContext } from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const darkSwitch = useDarkSwitch({
    onChange: (isDark) => {
      // You can perform side effects here, like updating your backend or analytics

      console.log('Theme changed:', isDark ? 'dark' : 'light');
    },
  });

  return (
    <ThemeContext.Provider value={darkSwitch}>
      <div className={darkSwitch.isDark ? 'dark' : 'light'}>{children}</div>
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

// Usage

const ThemedButton = () => {
  const { isDark, toggle } = useTheme();

  return (
    <button
      onClick={toggle}
      className={`px-4 py-2 rounded ${
        isDark ? 'bg-white text-black hover:bg-gray-200' : 'bg-black text-white hover:bg-gray-800'
      }`}
    >
      Toggle Theme
    </button>
  );
};

const App = () => (
  <ThemeProvider>
    <ThemedButton />
  </ThemeProvider>
);

8. Multi-Theme Selector with CSS Variables

import React, { useState, useEffect } from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const themes = {
  light: {
    '--bg-color': '#ffffff',

    '--text-color': '#000000',

    '--primary-color': '#3498db',
  },

  dark: {
    '--bg-color': '#1a1a1a',

    '--text-color': '#ffffff',

    '--primary-color': '#3498db',
  },

  sepia: {
    '--bg-color': '#f1e7d0',

    '--text-color': '#433422',

    '--primary-color': '#d95d39',
  },
};

const MultiThemeSelector = () => {
  const { isDark, setMode } = useDarkSwitch();

  const [currentTheme, setCurrentTheme] = useState(isDark ? 'dark' : 'light');

  useEffect(() => {
    const root = document.documentElement;

    Object.entries(themes[currentTheme]).forEach(([property, value]) => {
      root.style.setProperty(property, value);
    });
  }, [currentTheme]);

  const handleThemeChange = (theme) => {
    setCurrentTheme(theme);

    setMode(theme === 'dark');
  };

  return (
    <div className="p-4" style={{ backgroundColor: 'var(--bg-color)', color: 'var(--text-color)' }}>
      <h2 className="text-2xl mb-4">Select a Theme</h2>

      <div className="flex space-x-4">
        {Object.keys(themes).map((theme) => (
          <button
            key={theme}
            onClick={() => handleThemeChange(theme)}
            className={`px-4 py-2 rounded ${
              currentTheme === theme ? 'bg-blue-500 text-white' : 'bg-gray-200 text-black'
            }`}
          >
            {theme.charAt(0).toUpperCase() + theme.slice(1)}
          </button>
        ))}
      </div>

      <p className="mt-4" style={{ color: 'var(--primary-color)' }}>
        This text uses the primary color of the selected theme.
      </p>
    </div>
  );
};

9. Animated Theme Switch with Framer Motion


import  React  from  'react';

import { useDarkSwitch } from  'use-dark-switch';

import { motion } from  'framer-motion';



const  AnimatedThemeSwitch = () => {

const { isDark, toggle } = useDarkSwitch();



return (

<motion.div

className={`p-8 rounded-lg ${isDark ? 'bg-gray-900' : 'bg-white'}`}

animate={{

backgroundColor:  isDark ? '#1a202c' : '#ffffff',

}}

transition={{ duration:  0.3 }}

>

<motion.button

onClick={toggle}

className={`px-4 py-2 rounded-full ${

isDark ? 'bg-yellow-400 text-gray-900' : 'bg-indigo-600 text-white'

}`}

whileHover={{ scale:  1.05 }}

whileTap={{ scale:  0.95 }}

>

<motion.span

animate={{ rotate:  isDark ? 0 : 180 }}

transition={{ duration:  0.5 }}

>

{isDark ? '☀️' : '🌙'}

</motion.motion.span>

Toggle Theme

</motion.button>

<motion.p

className="mt-4"

animate={{

color:  isDark ? '#ffffff' : '#1a202c',

}}

transition={{ duration:  0.3 }}

>

Current theme: {isDark ? 'Dark' : 'Light'}

</motion.p>

</motion.div>

);

};

10. Theme-Aware Chart Component (using Chart.js)

import React, { useEffect, useRef } from 'react';

import { useDarkSwitch } from 'use-dark-switch';

import Chart from 'chart.js/auto';

const ThemeAwareChart = () => {
  const { isDark } = useDarkSwitch();

  const chartRef = useRef(null);

  const chartInstance = useRef(null);

  useEffect(() => {
    if (chartInstance.current) {
      chartInstance.current.destroy();
    }

    const ctx = chartRef.current.getContext('2d');

    chartInstance.current = new Chart(ctx, {
      type: 'bar',

      data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],

        datasets: [
          {
            label: '# of Votes',

            data: [12, 19, 3, 5, 2, 3],

            backgroundColor: isDark
              ? [
                  'rgba(255, 99, 132, 0.8)',
                  'rgba(54, 162, 235, 0.8)',
                  'rgba(255, 206, 86, 0.8)',

                  'rgba(75, 192, 192, 0.8)',
                  'rgba(153, 102, 255, 0.8)',
                  'rgba(255, 159, 64, 0.8)',
                ]
              : [
                  'rgba(255, 99, 132, 0.2)',
                  'rgba(54, 162, 235, 0.2)',
                  'rgba(255, 206, 86, 0.2)',

                  'rgba(75, 192, 192, 0.2)',
                  'rgba(153, 102, 255, 0.2)',
                  'rgba(255, 159, 64, 0.2)',
                ],

            borderColor: isDark
              ? [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)',

                  'rgba(75, 192, 192, 1)',
                  'rgba(153, 102, 255, 1)',
                  'rgba(255, 159, 64, 1)',
                ]
              : [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)',

                  'rgba(75, 192, 192, 1)',
                  'rgba(153, 102, 255, 1)',
                  'rgba(255, 159, 64, 1)',
                ],

            borderWidth: 1,
          },
        ],
      },

      options: {
        responsive: true,

        scales: {
          y: {
            beginAtZero: true,

            ticks: {
              color: isDark ? '#ffffff' : '#666666',
            },

            grid: {
              color: isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)',
            },
          },

          x: {
            ticks: {
              color: isDark ? '#ffffff' : '#666666',
            },

            grid: {
              color: isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)',
            },
          },
        },

        plugins: {
          legend: {
            labels: {
              color: isDark ? '#ffffff' : '#666666',
            },
          },
        },
      },
    });

    return () => {
      if (chartInstance.current) {
        chartInstance.current.destroy();
      }
    };
  }, [isDark]);

  return (
    <div className={`p-4 ${isDark ? 'bg-gray-800' : 'bg-white'}`}>
      <canvas ref={chartRef} />
    </div>
  );
};

Best Practices and Tips

  1. Use CSS Variables for Flexible Theming
:root {
  --bg-primary: #ffffff;

  --text-primary: #000000;

  --accent-color: #3498db;
}

.dark {
  --bg-primary: #1a1a1a;

  --text-primary: #ffffff;

  --accent-color: #3498db;
}

body {
  background-color: var(--bg-primary);

  color: var(--text-primary);
}

.accent {
  color: var(--accent-color);
}
  1. Implement a Theme Toggle Component
import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeToggle = () => {
  const { isDark, toggle } = useDarkSwitch();

  return (
    <button
      onClick={toggle}
      className={`p-2 rounded-full ${
        isDark ? 'bg-yellow-400 text-gray-900' : 'bg-gray-800 text-white'
      }`}
      aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`}
    >
      {isDark ? '🌞' : '🌙'}
    </button>
  );
};
  1. Handle Initial Page Load Flash

To prevent a flash of unstyled content when the page first loads, you can add a script to your HTML that sets the initial theme based on the user's preference:

<!DOCTYPE html>

<html lang="en">
  <head>
    <script>
      (function () {
        const darkMode = localStorage.getItem('dark-mode') === 'true';

        document.documentElement.classList.toggle('dark', darkMode);
      })();
    </script>

    <!-- Other head elements -->
  </head>

  <body>
    <!-- Your app content -->
  </body>
</html>
  1. Use Media Queries for System Preference

You can use CSS media queries to detect the user's system preference for dark mode:

@media (prefers-color-scheme: dark) {
  :root {
    --bg-primary: #1a1a1a;

    --text-primary: #ffffff;
  }
}

@media (prefers-color-scheme: light) {
  :root {
    --bg-primary: #ffffff;

    --text-primary: #000000;
  }
}
  1. Implement Smooth Transitions

To create a smooth transition between themes, add transition properties to your CSS:

body {
  transition: background-color 0.3s ease, color 0.3s ease;
}

.theme-transition * {
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

Advanced Topics

Server-Side Rendering (SSR) Considerations

When using useDarkSwitch with SSR frameworks like Next.js, you need to handle the initial render carefully to avoid hydration mismatches. Here's an example of how to do this:

import React, { useEffect, useState } from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeWrapper = ({ children }) => {
  const [mounted, setMounted] = useState(false);

  const { isDark } = useDarkSwitch();

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return <div style={{ visibility: 'hidden' }}>{children}</div>;
  }

  return <div className={isDark ? 'dark' : 'light'}>{children}</div>;
};

export default ThemeWrapper;

Performance Optimization

To optimize performance when using useDarkSwitch, consider the following:

  1. Memoize components that don't need to re-render on theme changes:
import React, { memo } from 'react';

const ExpensiveComponent = memo(({ data }) => {
  // Render expensive component
});

export default ExpensiveComponent;
  1. Use the useMemo hook for computed values based on the theme:
import React, { useMemo } from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const ThemeAwareComponent = () => {
  const { isDark } = useDarkSwitch();

  const themeStyles = useMemo(
    () => ({
      backgroundColor: isDark ? '#1a1a1a' : '#ffffff',

      color: isDark ? '#ffffff' : '#000000',
    }),
    [isDark],
  );

  return <div style={themeStyles}>{/* Component content */}</div>;
};

Accessibility Considerations

When implementing dark mode, it's crucial to maintain good accessibility practices:

  1. Ensure sufficient color contrast in both light and dark modes.

  2. Use semantic HTML elements and ARIA attributes where appropriate.

  3. Provide a clear visual indication of the current theme and how to switch it.

Here's an example of an accessible theme toggle:

import React from 'react';

import { useDarkSwitch } from 'use-dark-switch';

const AccessibleThemeToggle = () => {
  const { isDark, toggle } = useDarkSwitch();

  return (
    <button
      onClick={toggle}
      aria-pressed={isDark}
      className={`p-2 rounded-lg ${isDark ? 'bg-gray-800 text-white' : 'bg-gray-200 text-black'}`}
    >
      <span className="sr-only">Toggle dark mode</span>

      <span aria-hidden="true">{isDark ? '🌙' : '☀️'}</span>
    </button>
  );
};

Troubleshooting

If you encounter issues while using useDarkSwitch, consider the following:

  1. Theme not persisting: Ensure that localStorage is available and not blocked by privacy settings.

  2. Flickering on page load: Implement the initial theme script as shown in the "Handle Initial Page Load Flash" section.

  3. Theme not syncing across tabs: The package should handle this automatically, but if issues persist, check for any conflicting localStorage manipulations in your app.

  4. Performance issues: Review the "Performance Optimization" section and ensure you're not causing unnecessary re-renders.

Conclusion


The useDarkSwitch package provides a flexible and powerful solution for implementing dark mode in React applications. By following these examples, best practices, and advanced topics, you can create a robust and user-friendly theming system for your project.

Remember to always consider accessibility, performance, and user preferences when implementing dark mode. With useDarkSwitch, you have the tools to create an excellent theme-switching experience that enhances your application's usability and appeal.

For more information, bug reports, or feature requests, please visit our GitHub repository or join our community discussions.