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.
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...
Find the complete project here.