DEV Community

Cover image for How to add Dark mode in Next.js Application using Tailwind CSS.
Rashid Ali
Rashid Ali

Posted on

How to add Dark mode in Next.js Application using Tailwind CSS.

Hey there, In today's article I am going to show you how to add Dark mode to your Next.js application using Tailwind CSS.

What is Next.js?

Next.js is a React 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 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

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, There you will be able to see the following default Next.js homepage.

Default nextjs homepage

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.

    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.

    @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.

    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 you can use Tailwind CSS IntelliSense 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.

  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.

npm install @heroicons/react next-themes
Enter fullscreen mode Exit fullscreen mode

Edit _app.js

Wrap the whole app with the ThemeProvider to use next-themes.

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;
Enter fullscreen mode Exit fullscreen mode

Then go to tailwind.config.js and add darkMode: 'class' after content array.

module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  darkMode: 'class',
  theme: {
    extend: {},
  },
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode
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.

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;
Enter fullscreen mode Exit fullscreen mode

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:
<p className="text-slate-800 dark:text-slate-300"
>
This is some text.
</p>
Enter fullscreen mode Exit fullscreen mode

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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

final product homepage

The image can be random because I am using Unsplash 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

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.

Discussion (0)