How to integrate Dark  Mode in Next Js 13 with Tailwind CSS

How to integrate Dark Mode in Next Js 13 with Tailwind CSS

Step-by-Step Guide: Adding Dark/Night Mode to Your Next.js 13 App using Tailwind CSS

Why use dark mode?

Dark mode is important in websites because it improves the user experience, reduces eye strain and fatigue, conserves energy and battery life, and enhances the overall design and aesthetic appeal of the website. Dark backgrounds make text and images stand out more, making it easier for users to read and interact with the website, and also creating a more immersive and visually appealing experience. Additionally, dark mode is easier on the eyes and can potentially improve sleep quality, especially in low-light environments. Dark mode has also become a popular design trend, making it important for websites looking to appeal to a younger, tech-savvy audience.

Is implementing Night mode in Next Js different from React?

Since Next Js has the power of SSG (Static Site Generation) & SSR (Server Side Rendering), so if you try to integrate night mode as you did in your previous projects in React or HTML/CSS it will not work properly and will throw errors. The reason is that in React or HTML/CSS, you set the theme modes (like "light", "dark" or "default") in the local storage of your browser and Next Js comes with Server Side Components, the server won't know what is happening inside your browser's local storage so the server sends the components on "default" mode. The default mode can be anything, it depends upon your code. After that when the server-side components/pages check the local storage and change its colors you will get an error "Error: Hydration failed because the initial UI does not match what was rendered on the server"

So to solve this you should not render your layouts and pages as client-side components*, they can throw some different kinds of errors*

The reason is that Page and Layout these two things were made in Next Js to create dynamic metadata, title, and a lot more, and those things are not possible to be generated by the client side.

Let's start by creating a Next Js Web App

Single line command to create a Next Js Project with Tailwind CSS

npx create-next-app@latest my-app

After running this command you'll face some questions

  1. Ok to proceed? (y) Enter

  2. Would you like to use TypeScript with this project? ... No / Yes Your Wish, I chose Yes

  3. Would you like to use ESLint with this project? ... No / Yes Choose Yes

  4. Would you like to use Tailwind CSS with this project? ... No / Yes Choose Yes

  5. Would you like to use src/ directory with this project? ... No / Yes Choose Yes

  6. Would you like to use experimental app/ directory with this project? ... No / Yes √ Choose Yes (not for production, only for learning the new features)

  7. What import alias would you like configured? ... @/* Your Wish, I chose No

After that, your Next Js Application will be created within a few seconds, let's move ahead and add some codes

Some Basic Concepts of Next Js 13

Now move to the my-app/src/app folder, here you will see layout.tsx and page.tsx files (layout.jsx and page.jsx files if you chose JavaScript).

The layout file helps to maintain a particular layout all over the website, for example, you can add a Header or Navbar inside the layout file then the Header or Navbar will be visible all over the website, no need to add the same component on every single page.

The page file is the web page file of that particular route, for example, /src/app/page.tsx indicates this URL http://localhost:3000/ and /src/app/manjesh/page.tsx indicates this URL http://localhost:3000/manjesh/ for a detailed explanation of the Next Js 13 routing system, you can check their documentation here

Let's start coding

Run those commands respectively one by one

cd my-app
npm install next-themes react-icons

This package next-themes will help you a lot to make this night mode feature work very quickly. Add the code given inside the tailwind.config.js file

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'class',
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx}',
    './src/components/**/*.{js,ts,jsx,tsx}',
    './src/app/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      backgroundImage: {
        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
        'gradient-conic':
          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
      },
    },
  },
  plugins: [],
}

Don't worry if this file is showing any errors, you just run the app it will work smoothly

Create a components folder inside the /src/app directory, then create a Themeprovider file inside the /src/app/components directory, remember the theme mode will be stored inside the local storage so it must be a client-side component. Let's see the code

/src/app/components/Themeprovider.tsx

"use client";

import { ThemeProvider } from "next-themes";
import { ReactNode } from "react";

const Providers = ({ children }: { children: ReactNode }) => {
  return (
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
      {children}
    </ThemeProvider>
  );
};

export default Providers;

Create a theme changer component so that a user can choose a theme on his preferences. This component also must be a client-side component since this will be dealing with the local storage of the browser

/src/app/components/Themechanger.tsx

"use client";

import React, { useEffect, useState } from "react";
import { BsMoonStarsFill, BsFillSunFill } from "react-icons/bs";
import { useTheme } from "next-themes";

const Themechanger = () => {
  const [mounted, setMounted] = useState(false);
  const { theme, setTheme } = useTheme();

  // useEffect only runs on the client, so now we can safely show the UI

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

  if (!mounted) {
    return null;
  }

  const light = theme === "light";
  return (
    <button className="fixed z-40 bottom-5 right-5 dark:bg-gray-900 dark:text-yellow-400 bg-gray-100 text-gray-900 w-10 h-10 rounded-full flex justify-center items-center">
      {light ? (
        <BsMoonStarsFill onClick={() => setTheme("dark")} size={27} />
      ) : (
        <BsFillSunFill onClick={() => setTheme("light")} size={27} />
      )}
    </button>
  );
};

export default Themechanger;

Here mounted state is created and useEffect hook is used together in this code to ensure that the component only renders on the client side after the useEffect hook has been completed.

The mounted state is initially set to false, and then the useEffect hook is used to update the mounted state to true. By default, useEffect only runs on the client side, so it ensures that the component will only render after the client has received the necessary data from the server side, so this can help you to stay away from errors.

Now let's edit the layout file

/src/app/layout.tsx

import "./globals.css";
import { Metadata } from "next";
import Providers from "./components/Themeprovider";
import Themechanger from "./components/Themechanger";

export const metadata: Metadata = {
  title: "Create Night Mode",
  description: "Manually Created",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <Providers>
          <div className="overflow-x-hidden">
            <Themechanger />
            {children}
          </div>
        </Providers>
      </body>
    </html>
  );
}

Now let's add some changes inside the page file then it's done

/src/app/page.tsx

import React from "react"

const page = () => {
  return (<div className='text-blue-900 text-3xl font-extrabold dark:text-yellow-300 w-full min-h-screen'>
      Hello World!
  </div>)
}

export default page

Now run the app

cd my-app
npm run dev

Let's see, woohoo 🥳🥳 it's working

Don't worry about the console warnings you see on the right side, it's due to the Grammarly extension, if you have some extensions like that you'll see those kinds of warning messages but they don't affect your web app. So don't worry and code your project

I hope this article was helpful to you thank you, and don't forget to follow me for more content like this and subscribe to my newsletter, share this article with your friends who are learning web development or Next Js, feel free to ask your doubts and comment your feedback, and have a good day✌️🤗