Dark mode with Tailwindcss in Next.js
Tags: react, tailwindcss, nextjs, javascript
Published: 2020-12-03 18:46:58



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

```jsx 
npx create-next-app dark-mode
```

**Tailwindcss** installation
```jsx 
yarn add -D tailwindcss postcss autoprefixer
```
and **next-themes** that will allow us to switch to dark mode
```jsx 
yarn add next-themes
```

Create a postcss.config.js file and paste the following configuration of postcss
```jsx
module.exports = {
  plugins:{
    tailwindcss: {},
    autoprefixer: {}
  }
};

```
Then create a tailwindcss.config.js file and add the configuration below
```jsx
module.exports = {
  darkMode: "class",
  purge: ["./components/**/*.{js,ts,jsx,tsx}", "./pages/**/*.{js,ts,jsx,tsx}"],
  theme: {},
  variants: {},
  plugins:[]
};
```
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

```jsx
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;
```

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.

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

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

export default MyApp;
```

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

```jsx
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>
  );
}
```

then start the server with
```jsx
yarn dev 
```

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**
```jsx
 import { useTheme } from "next-themes" 
```

and we will extract theme and setTheme
```jsx
 const { theme, setTheme } = useTheme(); 
```

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

```jsx 
const [isMounted, setIsMounted] = useState(false);
```

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

```jsx
useEffect(() => {
    setIsMounted(true);
  }, []);
```

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

```jsx
const switchTheme = () => {
    if (isMounted) {
      setTheme(theme === "light" ? "dark" : "light");
    }
};
```

the full code of index.js
```jsx
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>
  );
}
```
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](https://dark-mode-tailwind-nextjs.vercel.app)

[Source code](https://github.com/enochndika/dark-mode-with-tailwind-and-nextjs)