Implementing Dark Mode in Next.js with Tailwind CSS

Implementing Dark Mode in Next.js with Tailwind CSS

Dark mode has become an increasingly popular feature in web applications. With Next.js and Tailwind CSS, adding dark mode to your application is a breeze.

In this post, we'll walk through how to implement dark mode in a Next.js application using Tailwind CSS and the next-themes library.

Result

Setting up the Project

To get started, we need a Next.js application with Tailwind CSS configured. You can use the following command to create a new Next.js app with Tailwind CSS:

   npx create-next-app -e with-tailwindcss appname

This will create a new Next.js app with Tailwind CSS configured.

Implementing Dark Mode

Next.js makes it easy to implement dark mode using the dark variant in Tailwind CSS. You can prepend dark: to properties to indicate that they are for dark mode. For example, you can use dark:bg-gray-900 to change the background color to gray-900 when the theme is set to dark.

By default, Next.js uses the system preferences to set the theme. However, we also need to toggle between light and dark modes. To do this, we can set the darkMode strategy to class in the tailwind.config.js file:

module.exports = {
  darkMode: 'class',
  // ...
}

With this configuration, Tailwind CSS will apply the theme when the dark class is present in the HTML tree.

Using next-themes

We need to toggle between and store the current theme somehow so that when user comes back it doesn't reset to default. One way of doing this could be with local storage and doing DOM manipulation.

In this blog, we'll do something way simpler and use next-themes which handles all the toggling and storing of our preference.

To install the library, run:

npm i next-themes

In the _app.js file, wrap your component with the ThemeProvider from next-themes. Set the attribute prop to class to allow the library to apply the theme to the HTML element.

import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ThemeProvider } from "next-themes"

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
  )
}

export default MyApp

Accessibility Considerations

It's important to ensure that your application is accessible in both light and dark mode. When choosing text and background colors, make sure that they have sufficient contrast. You can test the accessibility of your application using a tool like the Accessibility Insights browser extension.

Adding some Global Styles

We can add some global styles to global.css. Here we set the background of the body to white in light mode and dark gray in dark mode.

@layer base{
    body {
        @apply dark:bg-gray-800 bg-white
    }
}

Adding a Toggle Button

To allow users to toggle between light and dark modes, we need to add a toggle button to our application.

We use the useTheme hook to know what the current theme is, change the current theme using the setTheme method, and the system theme.

Initially, we set the theme to the system theme.

The onclick event on the button toggles between the themes. We add some individual class using the dark: variant for the button.

"use client"
import React from 'react'
import { useTheme } from "next-themes";


const Button = () => {
    const { systemTheme, theme, setTheme } = useTheme();
    const currentTheme = theme === 'system' ? systemTheme : theme;

    return (
        <button
            onClick={() => theme == "dark"? setTheme('light'): setTheme("dark")}
            className='bg-gray-800 dark:bg-gray-50 hover:bg-gray-600 dark:hover:bg-gray-300 transition-all duration-100 text-white dark:text-gray-800 px-8 py-2 text-2xl md:text-4xl rounded-lg absolute bottom-32'>
            Toggle Mode
        </button>
    )
}

export default Button

Adding Elements to Index.tsx

We add the toggle button to index.tsx in the app directory.


      <main className="flex items-center justify-center h-screen flex-col">
        <h2 className={`${roboto.className} text-4xl sm:text-6xl md:text-9xl text-center text-gray-800`}>LIGHT MODE</h2>
        <h2 className={`${roboto.className} text-4xl sm:text-6xl md:text-9xl text-center text-white `}>DARK MODE</h2>
        <Button/>
      </main>

Voila! Let there be light... cat meme

Find the complete project here.

Did you find this article valuable?

Support Chinmay Mhatre by becoming a sponsor. Any amount is appreciated!