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
Tailwindcss installation
yarn add -D tailwindcss postcss autoprefixer
and next-themes that will allow us to switch to dark mode
yarn add next-themes
Create a postcss.config.js file and paste the following configuration of postcss
module.exports = {
plugins:{
tailwindcss: {},
autoprefixer: {}
}
};
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:[]
};
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;
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;
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>
);
}
then start the server with
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
import { useTheme } from "next-themes"
and we will extract theme and setTheme
const { theme, setTheme } = useTheme();
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);
and we will set isMounted to true when the component is mounted.
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.
const switchTheme = () => {
if (isMounted) {
setTheme(theme === "light" ? "dark" : "light");
}
};
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>
);
}
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.
Top comments (3)
Wow! With the help of next-theme that was easy!
Josh Comeau states in this blog article that implementing dark mode was the hardest part in creating the entire website :D
joshwcomeau.com/react/dark-mode/
Thanks, this helped me a lot.
Have you been able to create a solution that allows users to select the theme and save it; without having the flash effect?