How to add Dark mode in Next.js Application using Tailwind CSS.
Tags: javascript, nextjs, css, tutorial
Published: 2022-05-16 02:34:43

Hey there, In today's article I am going to show you how to add Dark mode to your [Next.js](https://nextjs.org/) application using [Tailwind CSS](https://tailwindcss.com/).

##What is Next.js?
Next.js is a [React](https://reactjs.org/) based open-source frontend framework, with features like SSR (Server Side Rendering) and SSG (Static Site Generation). Next.js also comes SEO optimized right out the box so you don't have to do it yourself from the scratch.

##What is Tailwind CSS?
Tailwind CSS is utility-first CSS framework for quickly building highly customizable User Interfaces with low level CSS, This is great because it provides you an easy way to implement your web design and it only ships the CSS that is being used on the webpage which makes the whole app more light and efficient.

##Here is how you can add Dark mode to your Next.js application using Tailwind CSS.

####Prerequisites:
Make sure you have [Nodejs](https://nodejs.org/) installed on your local machine.

### 1. Setting up a new Next.js project
You can skip this step if you already have one.
Run `npx create-next-app nextjs-tailwind` in your terminal that should give you a new Next.js project with following files, You can use what ever name you prefer instead of `nextjs-tailwind`.

![Next.js file structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dpscabzae8folovyx43.PNG)

Run `cd nextjs-tailwind` to change directory and run `npm run dev` or `yarn dev` if you are using yarn to start your local developement server.

Go to [http://localhost:3000](http://localhost:3000), There you will be able to see the following default Next.js homepage.

![Default nextjs homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8sg0hz65lkx1e2m95xcx.png)
### 2. Adding Tailwind CSS
1. Run `npm install -D tailwindcss postcss autoprefixer`, It will install Tailwind CSS and all the necessary dependencies.

2. Then run `npx tailwindcss init -p`, It will create _`tailwind.config.js`_ and _`postcss.config.js`_ files.

3. Open _`tailwind.config.js`_ and do the following code changes.```javascript
module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```It will let Tailwind CSS know that on which files or folders it should be applied.

4. Now open _`styles/globals.css`_ and add the following tailwind directives.```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

5. Spin up you development server by running `npm run dev`.

6. Make following changes into your _`pages/index.js`_ to see if Tailwind CSS is working.```jsx
export default function Home() {
  return (
    <div className="pt-6 mt-28">
      <div className="flex justify-center items-center flex-col ">
        <div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md mt-10">
          <a href="#">
            <img
              className="rounded-t-lg "
              src="https://source.unsplash.com/random/1920x1080/?laptop"
              alt=""
            />
          </a>
          <div className="p-5">
            <a href="#">
              <h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900">
                Laptops that you can't afford even in 2022.
              </h5>
            </a>
            <p className="mb-3 font-normal text-gray-700">
              Lorem ipsum dolor sit amet. Cum laudantium laborum ut saepe
              facilis aut rerum corporis qui debitis voluptas. Rerum dolores aut
              voluptas galisum aut iure repellendus.
            </p>
            <a
              href="#"
              className="inline-flex items-center py-2 px-3 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300"
            >
              Read more
              <svg
                className="ml-2 -mr-1 w-4 h-4"
                fill="currentColor"
                viewBox="0 0 20 20"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  fillRule="evenodd"
                  d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
                  clipRule="evenodd"
                ></path>
              </svg>
            </a>
          </div>
        </div>
      </div>
    </div>
  );
}
```After this you should be able see a card with a image and some text.

> If you are using [VS CODE](https://code.visualstudio.com/) you can use [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) for better development experience.

### 3. Adding Dark mode
To add dark mode we need to install few things.
1. `next-themes`, This package provides an easy to implement themes in Next.js, You can check more on that [here](https://www.npmjs.com/package/next-themes).

2. This package is optional you can use text or SVGs directly if you want but I will install `@heroicons/react`, It is an icon library which I will be using to make a theme toggle button.

Run the following command to install above packages.
```bash
npm install @heroicons/react next-themes
```

#### Edit _`_app.js`_
Wrap the whole app with the `ThemeProvider` to use `next-themes`.
```jsx
import '../styles/globals.css';
import { ThemeProvider } from 'next-themes';

function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider enableSystem={true} attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

export default MyApp;
```
Then go to _`tailwind.config.js`_ and add `darkMode: 'class'` after content array.
```javascript
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  darkMode: 'class',
  theme: {
    extend: {},
  },
  plugins: [],
};
```
#####Note:
> Technically you can hard code `darkMode` in _`tailwind.config.js`_ and in the attribute of the `ThemeProvider` but since we want it to have state so I am using class. It will update HTML head and tailwind with the current theme using the ThemeProvider's context.

### 4. Creating the theme toggle component.
Create a `src` folder in root directory of the project and inside that create a file with name _`ThemeToggler.js`_, Add following code.
```jsx
import { useTheme } from 'next-themes';
import { useState, useEffect } from 'react';
import { MoonIcon, SunIcon } from '@heroicons/react/outline';

const ThemeToggler = () => {
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);
  if (!mounted) return null;
  return (
    <button
      className="w-8 h-8 bg-blue-100 rounded-lg dark:bg-slate-800 flex items-center justify-center hover:ring-2 ring-blue-400 transition-all duration-300 focus:outline-none"
      onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
      aria-label="Toggle Dark Mode"
    >
      {theme === 'light' ? (
        <MoonIcon className="text-blue-500 w-5 h-5" />
      ) : (
        <SunIcon className="text-blue-400 w-5 h-5" />
      )}
    </button>
  );
};

export default ThemeToggler;
```
#### Let me break it down for you
1. I am using `useTheme` hook from `next-themes` that provides two things, one is `theme` which is just a string representing current theme and `setTheme` which is a method to change the current theme. It is similar to `useState` hook.

2. Since our component gets rendered on server first so we have some hydration mismatch problem, so in order to solve that I am checking if the component is mounted on the client side using React's `useEffect` hook.

3. In last I am using sun and moon icon to indicate actions with a simple JSX ternary operator.

#### How to add tailwind utility classes for dark mode.
So the way tailwind CSS dark mode works is similar to hover feature, once you have decided how you want the element to look in dark mode you can just add `dark:` in front of any utility classes that you wish to be activated in dark mode only.
##### Example:
```jsx
<p className="text-slate-800 dark:text-slate-300"
>
This is some text.
</p>
```
so text color will be slate-800 if in light mode and slate-300 if it is in dark mode.

#### Final steps
Now lets add `ThemeToggler` and some classes for dark mode in our homepage, Open _`pages/index.js`_ and add following code.
```jsx
import ThemeToggler from '../src/ThemeToggler';

export default function Home() {
  return (
    <div className="pt-6 mt-28">
      <div className="flex justify-center items-center flex-col ">
        <ThemeToggler />
        <div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700 mt-10 transition duration-300">
          <a href="#">
            <img
              className="rounded-t-lg "
              src="https://source.unsplash.com/random/1920x1080/?laptop"
              alt=""
            />
          </a>
          <div className="p-5">
            <a href="#">
              <h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
                Laptops that you can't afford even in 2022.
              </h5>
            </a>
            <p className="mb-3 font-normal text-gray-700 dark:text-gray-400">
              Lorem ipsum dolor sit amet. Cum laudantium laborum ut saepe
              facilis aut rerum corporis qui debitis voluptas. Rerum dolores aut
              voluptas galisum aut iure repellendus.
            </p>
            <a
              href="#"
              className="inline-flex items-center py-2 px-3 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
            >
              Read more
              <svg
                className="ml-2 -mr-1 w-4 h-4"
                fill="currentColor"
                viewBox="0 0 20 20"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  fillRule="evenodd"
                  d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
                  clipRule="evenodd"
                ></path>
              </svg>
            </a>
          </div>
        </div>
      </div>
    </div>
  );
}
```

If you have done every thing as mentioned you should be able to see something like below on the homepage.


![final product homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5vcper608mpkrm4rn90.gif)

The image can be random because I am using [Unsplash](https://unsplash.com/) API.
Also if you open browser's dev tools, You will be able to see the current state of theme on your local storage.

![chrome dev tools showing local storage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asgob11pvjwk8awml84e.PNG)

>Thanks for reading, I hope you got something out of it, if you have any questions or suggestions, please feel free to comment below also don't forget to follow me on twitter.