DEV Community

Cover image for Dark-Light Mode in Next Js
Muhammed Sabith
Muhammed Sabith

Posted on

Dark-Light Mode in Next Js

How To Implement Light/Dark mode in Next Js

This is a brief tutorial on how to implement dark & light mode in next.Js

special thanks to developerAromal

Prerequisites

  • next-themes
  • next js project
  • lucide-react icons (only if you're using sun/moon toggle icons)

Steps

  1. Create a new Next Js project using the command
npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode
  1. Inside the project folder, install next-theme and lucide-react (it's for creating the Moon/Sun Icon)
npm i next-themes
Enter fullscreen mode Exit fullscreen mode

install lucide-react, if you want to show sun & moon icon

npm i lucide-react
Enter fullscreen mode Exit fullscreen mode
  1. Create a folder inside app directory named components inside components, create two folders ui and includes. Inside ui goes the files : theme-provider.tsx & theme-switcher.tsx. Inside includes we create a Navbar.tsx component, ( for putting the light/dark mode toggle )

the folder structure should look like this:

/app
└── /components
    ├── /ui
    │   ├── theme-provider.tsx 
    │   └── theme-switcher.tsx 
    └── /includes
        └── navbar.tsx
Enter fullscreen mode Exit fullscreen mode
  1. Inside theme-provider.tsx put this code:
"use client";

import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";

export function ThemeProvider({
  children,
  ...props
}: React.ComponentProps<typeof NextThemesProvider>) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

Enter fullscreen mode Exit fullscreen mode
  1. Inside theme-switcher.tsx put this code:
"use client";

import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";

export const ThemeSwitcher = () => {
  const { setTheme } = useTheme();

  return (
    <main className="flex gap-6">
      <button onClick={() => setTheme("light")}>light</button>
      <button onClick={() => setTheme("dark")}>dark</button>
    </main>
  );
};

Enter fullscreen mode Exit fullscreen mode

you can see that, inside theme-switcher.tsx comes the toggle functionality, so what you can do is you can put the lucide react icons for sun and moon respectively in the code, to show both icons. Or as you wish ( your creativity !)

  1. Now call this theme-switcher.tsx in you Navbar.tsx :
"use client";

import { ThemeSwitcher } from "../ui/theme-switcher";

export default function Navbar() {
  return (
    <header className="bg-white dark:bg-black">
      <ThemeSwitcher />
    </header>
  );
}

Enter fullscreen mode Exit fullscreen mode

I've implemented a simple navbar, you can create a big one as you wish

nb:- the tailwind class on header tag

  1. Now inside globals.css :
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));

body {
  @apply bg-white text-black dark:bg-black dark:text-white;
}

Enter fullscreen mode Exit fullscreen mode

so what it basically does is the body gets white bg and black text (in light mode) and black bg and white text (in dark mode)

Since tailwind css is also used, you can do the following also :

<div className="bg-white dark:bg-blue-700">
    <h1>Hello world </h1>
</div>
Enter fullscreen mode Exit fullscreen mode
  1. All this won't work until you do the following thing, that is:
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

// add this 
import { ThemeProvider } from "./components/ui/theme-provider";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className="light"
      style={{ colorScheme: "light" }}
      suppressHydrationWarning
    >
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ThemeProvider
          attribute="class"
          defaultTheme="light"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Enter fullscreen mode Exit fullscreen mode

you need to wrap the children in the layout.tsx inside ThemeProvider and also do the following:

  • add suppressHydrationWarning to html element
  • wrap the {children} in ThemeProvider with the above attributes
  1. Now call the Navbar.tsx and add some dummy contents to page.tsx to see if it's working or not:
import Navbar from "./components/includes/Navbar";

export default function Home() {
  return (
    <section
      id="main"
      className="min-h-screen p-20 flex flex-col-reverse items-center justify-center"
    >
      <Navbar />
      <h1 className="text-center mb-20">
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere fugit
        quam quae in aut odit veniam quaerat, sapiente similique incidunt quis
        consequatur nulla obcaecati dolorum ipsam enim possimus perspiciatis
        sit.
      </h1>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode
  1. Try clicking on the light/dark mode to see the changes

Hope it worked !

Extra Help

check out the github repo to try yourself :
next-js-dark-light-mode

give this repo a star ⭐ if this helped you !

Again special thanks to developerAromal for helping

Check out his dev.to account : @developeraromal

Ask your doubts in the comments 😎🫵

Top comments (0)