React Tailwind dark mode switch button

A very simple and easy-to-implement dark and light mode button switch built with React using Tailwind CSS.

This method uses a state to check if the theme is set to dark mode and will remember your preference for the next visit.

You will need Tailwind CSS and React JS.

Full source code on CodePen.

This Javascript needs to be called very early as it will prevent the page from flashing as light mode before realising it should be dark and switching.

"dark" !== localStorage.getItem("color-theme") && ("color-theme" in localStorage || !window.matchMedia("(prefers-color-scheme: dark)").matches)
 ? (document.documentElement.classList.remove("dark"), document.documentElement.classList.add("light")) 
: (document.documentElement.classList.add("dark"), document.documentElement.classList.remove("light"));

It checks if you already have a theme set previously or if the system has a preference.

Now for the switcher function and the icons. The state isDarkTheme gets initialized with a check on the body containing the class dark.

In processThemeChange() you can see the body gets assigned the class dark if the theme is set to be dark.

setIsDarkTheme(isDarkTheme => !isDarkTheme); is a state toggle, if it was previously true then it gets changed to false and vice versa.

The process theme change function gets called from an onClick handler on the icon button.

const App = () => {
    const [isDarkTheme, setIsDarkTheme] = useState((document.documentElement.classList.contains('dark')));

    function processThemeChange() {
        setIsDarkTheme(isDarkTheme => !isDarkTheme);
        if (localStorage.getItem('color-theme')) {
            if (localStorage.getItem('color-theme') === 'light') {
                document.documentElement.classList.add('dark');
                localStorage.setItem('color-theme', 'dark');
            } else {
                document.documentElement.classList.remove('dark');
                localStorage.setItem('color-theme', 'light');
            }
        } else {
            if (document.documentElement.classList.contains('dark')) {
                document.documentElement.classList.remove('dark');
                localStorage.setItem('color-theme', 'light');
            } else {
                document.documentElement.classList.add('dark');
                localStorage.setItem('color-theme', 'dark');
            }
        }
    }

    function switchTheme() {
        processThemeChange();
    }

    return (
        <div className={`${isDarkTheme ? 'bg-gray-800' : 'bg-white/50'} flex items-center justify-center h-screen`}>
            <h1 className={`${isDarkTheme ? 'text-gray-100' : 'text-gray-900'} text-xl p-2`}>{(isDarkTheme) ? "Make it light" : "Make it dark"}</h1>
            <button id="theme-toggle" onClick={switchTheme} type="button"
                    className={`${isDarkTheme ? 'text-gray-300 border-gray-300' : 'text-gray-800 border-gray-500'} border-2 rounded-lg text-sm p-2`}>
                <svg id="theme-toggle-dark-icon"
                     className={`${isDarkTheme ? 'hidden' : ''} w-5 h-5`} fill="currentColor"
                     viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                    <path
                        d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
                </svg>
                <svg id="theme-toggle-light-icon"
                     className={`${isDarkTheme ? '' : 'hidden'} w-5 h-5`} fill="currentColor"
                     viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                    <path
                        d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
                        fillRule="evenodd" clipRule="evenodd"></path>
                </svg>
            </button>
        </div>
    );
};

Each class name has a conditional to check if the dark theme is set, then shape the classes based on this.

Quick note: Usually I would make use of darkMode: 'class' in the Tailwind config however this cannot be done in Codepen. With this method you do not have to use a condition in the className instead just define the dark mode classes with dark: eg dark:bg-gray-800.

Tailwind React dark mode switch button