DEV Community

raivikas
raivikas

Posted on • Originally published at nextjsdev.com on

Add Dark mode in Nextjs App using Tailwind CSS Dark Mode.

Add Dark mode in Nextjs App using Tailwind CSS Dark Mode.

Hello everyone, I hope you are good and doing well. Today, we are going to discuss how we can add dark mode in Next.js App using Tailwind CSS dark mode in just 5 easy steps. So, Let's begin.

Having a bright screen at night is painful for our eyes. That's why the dark mode feature is necessary if we want to take care of our user's eyes.

In this lesson, we'll learn how to enable dark mode in our Next.js application to keep our users engaged with our app all night long.

We will see how we can easily implement the dark mode feature by using and combining next-themes and Tailwind CSS.

And we'll add the ability for our users to toggle between light and dark mode from the user interface.

Here is the image of the website:

Add Dark mode in Nextjs App using Tailwind CSS Dark Mode.

Add Dark mode in Nextjs App using Tailwind CSS Dark Mode.

Tech Stack used:

  1. Next.js
  2. Tailwind CSS
  3. React.js

Npm Packages used:

  1. next-themes
  2. @heroicons/react

Here is a little demo of the project, that we are going to build and the GitHub link of the project also.

Demo Link of the Project

Github Link of the Project

Step-1 Create Nextjs App and add Tailwind CSS.

First of all, create a Next.js project and add Tailwind CSS in it or you can create a new Next.js App with Tailwind CSS added in it already using this command.

npx create-next-app my-project // without Tailwind CSS installed
or
npx create-next-app -e with-tailwindcss my-project //with Tailwind CSS

Enter fullscreen mode Exit fullscreen mode

Now you have to install two packages next-themes and @heroicons/react from npm.

npm install next-themes @heroicons/react

Enter fullscreen mode Exit fullscreen mode

Step-2 Creating Navbar and Hero Section.

Let's create a simple Navbar, with a Hero section so that we can look at the changes of dark mode and light mode themes.

So, first of all, create two folders named components and sections in the root of the directory.

Inside the components , create a file name Logo.js and paste this code inside that.

Inside Logo.js write this code:

import { LightningBoltIcon } from "@heroicons/react/solid";
import Link from "next/link";

const Logo = () => {
    return (
        <Link href="/">
        <a className="my-2 flex items-center space-x-1 text-indigo-500">
          <LightningBoltIcon className="h-8 w-8 flex-shrink-0 mr-3"/>
          <span className="font-bold text-3xl font-sans tracking-tight whitespace-nowrap">Next-js Dev</span>
        </a>      
        </Link>
    )
}

export default Logo;
Enter fullscreen mode Exit fullscreen mode

Then create three files inside the section folder.

  1. Header.js
  2. Layout.js
  3. Footer.js

The file structure will look like this:

Add Dark mode in Nextjs App using Tailwind CSS Dark Mode.
File structure of the Nextjs App

Inside the Header.js write this code:

import Logo from "../components/Logo";

const Header = () => {

  return (
    <header className="h-15 shadow-sm dark:border-gray-700">
      <div className="container px-4 sm:px-6 py-4 flex justify-between items-center">
        {/* Logo */}
        <Logo />
      </div>
    </header>
  );
};

export default Header;

Enter fullscreen mode Exit fullscreen mode

Inside the Footer.js write this code:

const Footer = () => {
  return (
    <footer className="px-4 sm:px-6 py-6 mt-24">
      <div className="text-center text-sm text-gray-500">
        <span className="dark:text-gray-100 text-gray-900 font-bold text-lg mr-2"> Next.js Dev</span> &copy; {new Date().getFullYear()} All Rights Reserved
      </div>
    </footer>
  );
};

export default Footer;

Enter fullscreen mode Exit fullscreen mode

Inside the Layout.js write this code:

import Header from "./Header";
import Footer from "./Footer";
import Head from "next/head";

const Layout = ({ children}) => {
  return (
    <>
      <Head>
        <title>Nextjs-Dev Blog</title>
        <link rel="icon" href="/favicon.ico" />

      </Head>

      <div className="min-h-screen mx-auto max-w-2xl flex flex-col">
        <Header />
        <main className="flex-grow container mx-auto px-4 sm:px-6">
          {children}
        </main>
        <Footer />
      </div>
    </>
  );
};

export default Layout;

Enter fullscreen mode Exit fullscreen mode

Step-3 Importing the ThemeProvider from next-themes.

Now, open the _app.js inside the pages folder and write the following code given below.

Inside _app.js file:

import "tailwindcss/tailwind.css";
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

After that open the tailwind.config.js file and then add a property darkMode in it and give a value "class" to it like this.

module.exports = {
  mode: "jit",
  purge: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
    "./sections/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: "class", 
  theme: {
    extend: {
    },
  },
  variants: {
    extend: {},
  },
  plugins: [],
};

Enter fullscreen mode Exit fullscreen mode

Now, whenever, the dark class is present earlier in the HTML tree, tailwind CSS will apply the dark styles, otherwise, it applies the light theme by default.

Step-4 Adding a toggle button to control dark mode manually.

Now let's add the ability to toggle the dark mode manually from the user interface.

Open the Header.js file from the sections folder, and import the **useTheme** hook from next-themes, along with the **MoonIcon** and the **SunIcon** components from heroicons.

Then, retrieve the **systemTheme**, the **theme**, and the **setTheme** properties by calling useTheme at the top of your component.

From there, create a new method names **renderThemeChanger** and get the current theme from the system or the theme variable.

Now, if the current theme is dark, we return the SunIcon component and implement the onClick event by using the setTheme method from next-themes to toggle the theme back to light on click to this icon.

Otherwise, we return the MoonIcon component and set the theme to dark on click.

Finally, we can call this new method right inside our JSX to render the current theme icon, and align it with our logo with flexbox.

The Header.js file now looks like this:

import Logo from "../components/Logo";
import {useTheme} from "next-themes";
import{SunIcon ,MoonIcon} from "@heroicons/react/solid";

const Header = () => {

  const {systemTheme , theme, setTheme} = useTheme ();

  const renderThemeChanger= () => {

      const currentTheme = theme === "system" ? systemTheme : theme ;

      if(currentTheme ==="dark"){
        return (
          <SunIcon className="w-10 h-10 text-yellow-500 " role="button" onClick={() => setTheme('light')} />
        )
      }

      else {
        return (
          <MoonIcon className="w-10 h-10 text-gray-900 " role="button" onClick={() => setTheme('dark')} />
        )
      }
   };

  return (
    <header className="h-15 shadow-sm dark:border-gray-700">
      <div className="container px-4 sm:px-6 py-4 flex justify-between items-center">
        {/* Logo */}
        <Logo />

        {renderThemeChanger()}
      </div>
    </header>
  );
};

export default Header;

Enter fullscreen mode Exit fullscreen mode

Step-5 To avoid hydration mismatch.

There is one more thing we need to do to avoid hydration mismatch.

Because we cannot know the theme on the server, the values returned from useTheme will be undefined until mounted on the client.

In other words, our theme icon will not match the actual current theme. And that's pretty bad for user experience.

So to fix this, we need to make sure we only render our icon when the page is mounted on the client.

So, import the useState and useEffect hooks from react.And create a new state variable to track if the component has been mounted or not on the client-side.

Set the initial value to false, and then set its value to true inside a useEffect hook.

Finally, inside the renderThemeChanger, check if the component is mounted before rendering the UI for the theme changer.

Inside Header.js file:

import {useState, useEffect} from "react";

const Header = () => {
const [mounted, setMounted] = useState(false);

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

const renderThemeChanger= () => {
      if(!mounted) return null;

      const currentTheme = theme === "system" ? systemTheme : theme ;

      if(currentTheme ==="dark"){
        return (
          <SunIcon className="w-10 h-10 text-yellow-500 " role="button" onClick={() => setTheme('light')} />
        )
      }

      else {
        return (
          <MoonIcon className="w-10 h-10 text-gray-900 " role="button" onClick={() => setTheme('dark')} />
        )
      }
   };
}

Enter fullscreen mode Exit fullscreen mode

Step-6 Time to add Dark Mode variant styles.

Now that everything is all set, let's add the styles to our elements using the dark variant from Tailwind CSS, save your file and refresh your browser.

Now navigate to the styles folder and open the global.css file.

Inside global.css that paste this code:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  body {
    @apply bg-gray-50 text-gray-900 dark:bg-black dark:text-gray-100;
    @apply min-h-screen;
  }

  ::selection {
    @apply text-white bg-indigo-500;
  }


  .btn{
    @apply inline-block px-5 py-3 rounded-lg hover:bg-indigo-600 transition duration-300 shadow-lg bg-indigo-500 text-white capitalize tracking-wider font-semibold text-sm sm:text-xl focus:outline-none focus:ring-4 focus:ring-indigo-500 focus:ring-opacity-50 whitespace-nowrap
  }
}

Enter fullscreen mode Exit fullscreen mode

You can now change the theme of your application manually from

the UI which is what we wanted.

You can easily change those dark variants by just adding classes like this:

<h1 className=" dark:bg-gray-900 dark:text-gray-100 " >This is H1 heading</h1>

Enter fullscreen mode Exit fullscreen mode

Conclusion

So that was all about adding Dark mode in Nextjs App using Tailwind CSS. I hope you like this and enjoyed building this project.

If you think that this was helpful and then please do consider visiting my original blog link and do follow me on Twitter and connect with me on LinkedIn.

If you were stuck somewhere and not able to find the solution you can check out my completed github repository here.

Thanks for your time to read this project, if you like this please share it on Twitter and Facebook or any other social media and tag me there.

Some Useful Link:

  1. Next.js and Tailwind Installation Docs
  2. Github link for project

Connect with me:

  1. Twitter Link
  2. LinkedIn link
  3. Facebook Link
  4. Github Link

Discussion (0)