Theme Switch NextJS 13

2023-10-071 minutes

Let's create a dark mode / theme switch for NextJS App Dir.

Creating a NextJS app

Open up the terminal in your desired folder, then run any of these commands as per your preferred package manager. (You can skip to the next section if you already have a NextJS 13 app)

npx create-next-app@latest
yarn create next-app
pnpm create next-app
bunx create-next-app

Complete the prompts.

Installing and setting up themes

Install next-themes:

npm i next-themes
yarn/pnpm/bun add next-themes

# Optional: Installing a icon library
npm i lucide-react
yarn/pnpm/bun add lucide-react

After those have been installed, you can open up the project dir in your favourite code editor. Navigate to /app and create a providers.tsx or providers.jsx file depending on your preferred language.

1// app/providers.tsx
2
3"use client";
4
5import { ThemeProvider } from "next-themes";
6
7const Providers = ({ children }: { children: React.ReactNode }) => {
8  return (
9    <ThemeProvider 
10      attribute="class" // Setting the theme using class="" in <html /> tag
11      enableSystem // For automatically setting the theme
12    >
13      {children}
14    </ThemeProvider>
15  );
16};
17
18export default Providers;

You can view more options for the <ThemeProvider /> here: https://github.com/pacocoursey/next-themes#api

Import the providers.tsx/jsx file into your layout.tsx/jsx file, then wrap your {children} in the providers.

1// app/layout.tsx
2
3import "./globals.css";
4
5import Providers from "./providers";
6
7export default function RootLayout({
8  children,
9}: {
10  children: React.ReactNode;
11}) {
12  return (
13    <html lang="en">
14      <body>
15        <Providers>
16          {children}
17        </Providers>
18      </body>
19    </html>
20  );
21}

Creating the theme switch

Now for the user to be able to switch themes, we can create a toggle or a button or a select. We going to be using a button component for switching themes in this post.

Start by creating a components folder in your root dir, inside that create a folder called theme-switch and a index.tsx/jsx file inside it. The folder layout should look like this:

components/
  - theme-switch/
    - index.tsx 

Open the index.tsx/jsx file, and let's start with the code.

1"use client";
2
3import { useTheme } from "next-themes";
4import { Moon, Sun, SunMoon } from "lucide-react"; // Optional, you can use other icon libraries too
5
6export const ThemeSwitch = () => {
7  const { theme, setTheme } = useTheme();
8
9  const handleTheme = () => {
10    setTheme(theme === "light" ? "dark" : "light");
11  };
12
13  return (
14    <button
15      shape="icon"
16      className="h-11 w-11 rounded-sm aspect-square p-2.5"
17      onClick={handleTheme}
18    >
19      {theme === "light" ? <Sun /> : theme == "dark" ? <Moon /> : <SunMoon />}
20    </button>
21  );
22};

That'd be it! You can now import this lets say a <NavBar /> component use it!

Styling

We can style our themes in different ways, I'm going to show by TailwindCSS and normal CSS. Starting with TailwindCSS, open your tailwind.config.ts/js file then add darkMode: "class", as one of the options in the config. It should look something like this:

1import type { Config } from "tailwindcss";
2
3const config: Config = {
4  darkMode: "class",
5  // Your other options
6};
7export default config;

Then you can define your styles using in your classNames by prefixing dark: before the styling for dark mode.

1// Some page or component
2
3export const Component = () => {
4  return <div className="rounded-xl p-4 bg-black text-white dark:bg-white dark:text-black" />;
5}

If you're using CSS variables, then you can use this instead. Open you app/global.css file then:

1:root {
2  --primary: azure;
3  --secondary: gray;
4}
5
6/* Changing the variables if theme is dark */
7html.dark {
8  --primary: gray;
9  --secondary: azure;
10}
11
12.body {
13  /* Will automatically change once theme is switched */
14  background: var(--secondary);
15  color: var(--primary)
16}

That's it!

Now you can create your super-cool website with support for both dark/light mode or any colour! Have fun!