How to add a Light/Dark mode button into a React or Next.js app using TailwindCSS
Tags: tailwindcss, nextjs, react, lightdarkmode
Published: 2023-11-14 03:53:33

In the rapidly advancing world of technology, user experience is at the forefront of design considerations. One aspect that has gained significant attention in recent years is the implementation of light and dark modes in digital interfaces. Originally considered a mere aesthetic preference, light and dark modes are now evolving from a nice-to-have feature to a virtual necessity, transforming the way users interact with digital platforms.

In this post I'll focus on adding color mode toggle to an existing Next.js or React app that has TailwindCSS setup already :)

### Step 1 - Enable Tailwind Dark Mode Class

Tailwind comes with a dark mode class, [read more on that here](https://tailwindcss.com/docs/dark-mode).

To enable it simply open `tailwind.config.ts` and add `darkMode: 'class'` to the configuration. Like so:

```javascript
//tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  darkMode: 'class', //<---------ADD THIS LINE
  ...
};
export default config
```

### Step 2 - Create a component to toggle between color modes

Create a `ColorModeToggle.tsx` file in your `/components` directory and copy paste the following code into it:

```javascript
'use client';

import { useEffect, useState } from 'react';

//Toggle logic
export default function ColorModeToggle() {
  const [checked, setChecked] = useState(false); //default state

  //Handles changing the color mode and stores state in localStorage
  const toggleDarkMode = () => {
    //Add/Remove dark class to the root element and from local storage
    if (checked) {
      setChecked(false);
      document.documentElement.classList.add('dark');
      localStorage.setItem('darkMode', 'enabled');
    } else {
      setChecked(true);
      document.documentElement.classList.remove('dark');
      localStorage.setItem('darkMode', 'disabled');
    }
  };

  //Get color mode state from localStorage, if there isn't one, then get users preferred color mode
  useEffect(() => {
    //Check if dark mode is enabled in local storage
    const darkMode = localStorage.getItem('darkMode');
    const prefersDarkMode =
      window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;

    //Set the checked state based on local storage value or by default user preference if there is no value in local storage
    if ((darkMode && darkMode === 'enabled') || (!darkMode && prefersDarkMode)) {
      document.documentElement.classList.add('dark');
      setChecked(true);
    }
  }, []);

  return (
      <button
        type='button'
        onClick={toggleDarkMode}
        className='h-8 w-8 rounded-lg p-1 hover:bg-gray-100 dark:hover:bg-gray-700'
      >
        <svg className='fill-violet-700 block dark:hidden' fill='currentColor' viewBox='0 0 20 20'>
          <path d='M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z'></path>
        </svg>
        <svg className='fill-yellow-500 hidden dark:block' fill='currentColor' viewBox='0 0 20 20'>
          <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>
  );
}

```
I've outlined what each method does in the comments. Once, you have added that to your project place the components wherever you'd like in your UI, then start styling.

Here is an example where I add the `ColorModeToggle` component to a `Nav` components: 

```javascript
import ColorModeToggle from '@/components/ColorModeToggle';

export default function Nav() {
  return (
    <nav className='flex justify-end'>
      <ColorModeToggle />
    </nav>
  )
}
```

#### Note: By default styles are in light mode. To apply specific styles in dark mode, use the dark class like so : `dark:{some tailwind style}`.

Here is an example:

```html
<div className="bg-white text-black dark:bg-black dark:text-white">...some content</div>
```

This will now set the background of that div to white, and the text color to black in light mode, and background to black and text color to white in dark mode.

That's it! If you have any questions or would like me to go into more details please leave a comment and I'll do my best to help :)

## Extra Tip:

You can use the `dark` class in your global css file by extending tailwind methods. This way you can add global styling for light and dark modes using tailwind. You can also create custom css classes that have both dark and light styles.

Here is an example:

```css 
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  section:nth-child(even) {
    @apply bg-white text-gray-800 dark:bg-slate-900 dark:text-white;
  }
  section:nth-child(odd) {
    @apply bg-gray-300 text-gray-900 dark:bg-slate-800 dark:text-white;
  }
}
```
I've extended tailwind base to apply tailwind styles globally to `section` tags. I also apply slightly different styles to odd and even section tags. Now all the section tags will have light and dark mode styles that can easily be toggled with the `ColorModeToggle` component. 

You can also do this to tailwind `components` and `utilities`. You can read more about adding custom styles to tailwind [here](https://tailwindcss.com/docs/adding-custom-styles).  

Thank you for reading. Hope this has helped you in some way!