DEV Community

Cover image for Dark mode with Tailwindcss in Next.js
enoch ndika
enoch ndika

Posted on • Updated on

Dark mode with Tailwindcss in Next.js

Version 2.0 of tailwindcss brings several new features, including dark mode support making it easier than ever to dynamically modify your application when dark mode is enabled.

We will start by creating a new Next.js application

npx create-next-app dark-mode
Enter fullscreen mode Exit fullscreen mode

Tailwindcss installation

yarn add -D tailwindcss postcss autoprefixer
Enter fullscreen mode Exit fullscreen mode

and next-themes that will allow us to switch to dark mode

yarn add next-themes
Enter fullscreen mode Exit fullscreen mode

Create a postcss.config.js file and paste the following configuration of postcss

module.exports = {
  plugins:{
    tailwindcss: {},
    autoprefixer: {}
  }
};

Enter fullscreen mode Exit fullscreen mode

Then create a tailwindcss.config.js file and add the configuration below

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

In this configuration, the change of theme will be done with the classes, which will facilitate integration with next-themes.

In the pages directory, create a new file _document.js and add the configuration below

import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head />
        <body className="bg-white text-black dark:bg-black dark:text-white">
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;
Enter fullscreen mode Exit fullscreen mode

At the body level, we defined the global classNames configuration. When the theme will be by default, the text color will be black and the background color white. When the dark mode will be triggered, the text color will be white and the background color will be black. You can modify them as you want

You can delete the styles directory.

Then in the _app.js file in the pages directory, we will import ThemeProvider from next-themes and we will also import tailwind.css.

import 'tailwindcss/tailwind.css';
import { ThemeProvider } from "next-themes";

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

export default MyApp;
Enter fullscreen mode Exit fullscreen mode

In the index.js file replace the initial content by this one

import Head from "next/head";

export default function Home() {
  return (
    <div className="text-center">
      <Head>
        <title>Dark mode with Tailwind and Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <h1 className="text:2xl">Dark mode with Tailwind and Next-themes</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

then start the server with

yarn dev 
Enter fullscreen mode Exit fullscreen mode

To switch to dark mode we will need useTheme which will be imported from next-themes. useTheme() contains several properties but what will interest us is theme, which returns the active theme and setTheme which allows you to change the theme.

The advantage of this library is that it avoids flash when loading the page on the server side because ThemeProvider automatically injects a script in next/head to update the html element with the correct attributes before loading the rest of the page. This means that the page will not flash under any circumstances.

we will import useTheme in index.js

 import { useTheme } from "next-themes" 
Enter fullscreen mode Exit fullscreen mode

and we will extract theme and setTheme

 const { theme, setTheme } = useTheme(); 
Enter fullscreen mode Exit fullscreen mode

As we are going to change the theme on client-side, we will first check if the component is mounted.

const [isMounted, setIsMounted] = useState(false);
Enter fullscreen mode Exit fullscreen mode

and we will set isMounted to true when the component is mounted.

useEffect(() => {
    setIsMounted(true);
  }, []);
Enter fullscreen mode Exit fullscreen mode

then we are going to define a function that will allow to change the theme by checking first if the component is mounted.

const switchTheme = () => {
    if (isMounted) {
      setTheme(theme === "light" ? "dark" : "light");
    }
};
Enter fullscreen mode Exit fullscreen mode

the full code of index.js

import { useEffect, useState } from "react";
import Head from "next/head";
import { useTheme } from "next-themes";
export default function Home() {
  const [isMounted, setIsMounted] = useState(false);
  const { theme, setTheme } = useTheme();
useEffect(() => {
    setIsMounted(true);
  }, []);
const switchTheme = () => {
    if (isMounted) {
      setTheme(theme === "light" ? "dark" : "light");
    }
  };
return (
    <div className="text-center">
      <Head>
        <title>
          Dark mode with Tailwind and Next.js
        </title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <h1 className="text:2xl">
       Dark mode with Tailwind and Next- themes
      </h1>
      <button onClick={switchTheme}>Change theme</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can refresh the page and you won't see any flashes.

Know that you can also implement the dark mode without tailwindcss, just with the next-themes library . it can be implemented with styled-components, emotion or with css classes.

Demo

Source code

Discussion (2)

Collapse
arnabxd profile image
Arnab Paryali

Thanks, this helped me a lot.

Collapse
jessycormier profile image
Jessy Cormier

Have you been able to create a solution that allows users to select the theme and save it; without having the flash effect?