Add a dark theme toggle to your Next.js and Tailwind app using React’s Context API

Siddharth
3 min readDec 25, 2020
Final Output

Note:

  • no dependencies used
  • Pure react

STEP 1 : Setting Up The Project

Since our project will be using Tailwind CSS so we start by downloading/cloning the with-tailwindcss example from nextjs Official repository.

npx create-next-app --example with-tailwindcss with-tailwindcss-app
# or
yarn create next-app --example with-tailwindcss with-tailwindcss-app

Then cd into the directory by typing the command

cd with-tailwindcss-app

And start the development server by further typing the command

yarn dev

Open up http://localhost:3000/ in your browser and you must see:

next js with tailwind css

now we are good to go for next step:

STEP 2: Adding The Toggle Button

Modify nav.js file inside of components directory by adding following lines inside the second <ul> tag

<li>   <div className=’btn-blue’>Toggle Theme</div></li>

navbar will look like:

nav bar will look like this

we will handle the click later after setting up the theme.

STEP 3: Set up class based dark theme For Tailwind

Open tailwind.config.js and change darkMode from “media” to “class” as:

darkMode: "class", // 'media' or 'class'

Open index.css inside styles directory and add min-height property to body as:

html,body {   min-height: 100vh; /* add this line */   @apply bg-gray-50 dark:bg-gray-900;}

STEP 4: Setting up Themeprovider (React Context Api)

  • Create a theme Directory in the root.
  • Inside theme directory create a file named ThemeContext.js
  • Paste the content below inside ThemeContext.js
import React, { createContext, useEffect, useState } from "react";const defaultState = {  dark: false,  toggleDark: () => {},};const ThemeContext = createContext(defaultState);const ThemeProvider = ({ children }) => {const [dark, setDark] = useState(false);useEffect(() => {  const lsDark = localStorage.getItem("dark");  if (lsDark !== null) {    setDark(JSON.parse(lsDark));  }}, []);const toggleDark = () => {  const d = document.documentElement;  const themes = ["light", "dark"];  if (dark) {    d.classList.remove(...themes);    d.classList.add("light");  } else {    d.setAttribute("class", "dark");  }  localStorage.setItem("dark", JSON.stringify(!dark));  setDark(!dark);};return (  <ThemeContext.Provider     value={{     dark,     toggleDark,   }}>    {children}  </ThemeContext.Provider>  );};export default ThemeContext;export { ThemeProvider };
  • create custom document by creating a file named _document.js inside pages directory and paste the content from below.
import Document, { Head, Html, Main, NextScript } from "next/document";class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html>
<Head>
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
var storageKey = 'dark';
var classNameDark = 'dark';
var classNameLight = 'light';
var d = document.querySelector('html');
function setClassOnDocumentBody(dark) {
d.classList.add(dark ? classNameDark : classNameLight);
d.classList.remove(dark ? classNameLight : classNameDark);
}
var localStorageTheme = null;
try {
localStorageTheme = localStorage.getItem(storageKey);
} catch (err) {}
var localStorageExists = localStorageTheme !== null;
if (localStorageExists) {
localStorageTheme = JSON.parse(localStorageTheme);
}
if (localStorageExists) {
setClassOnDocumentBody(localStorageTheme);
} else {
var isDarkMode = d.classList.contains(classNameDark);
localStorage.setItem(storageKey, JSON.stringify(isDarkMode));
}
})();
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
  • wrap the whole app with ThemeProvider by updating the _app.js to same as below
import "../styles/index.css";
import { ThemeProvider } from "../theme.js/ThemeContext";
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
);
}
export default MyApp;

STEP 5(Final Step): Adding ability to access and change the theme

Now we can access dark and toggleDark using useContext Hook we’ll use it as:

add the below line inside nav component:

const { dark, toggleDark } = useContext(ThemeContext);

and also update the toggle button to:

<li>    <div className='btn-blue' onClick={() => toggleDark()}>       {dark ? "Light" : "Dark"} Theme    </div></li>

Hooray! we just added a dark theme toggle to our nextjs and tailwind app

Here’s another version with Redux.

DEMO: https://nextjs-tailwind-redux-dark.vercel.app/

Github Repo: https://github.com/siddsarkar/nextjs-tailwind-redux-dark

Till then Peace ✌

--

--