dark mod in Next.js 13+ using next-themes and Tailwind CSS while keeping the SSR intact

dark mod in Next.js 13+ using next-themes and Tailwind CSS while keeping the SSR intact

if you want a fast-paced tutorial, visit my article at medium !

ok, so I've made this article because the web was full of wrong or old data, and using state management techniques would just slow the website down and was unnecessary (I don't have that many side-by-side components nor do I need to control auth my self ) and could have been handled more efficiently, not to say that the majority of online guides didn't keep SSR and just YOLOed it and made all of the components client base!

Please let me know if you find anything with the method I've gathered or have any suggestions!

OK first things first , install the next project with Tailwind or just install tailwind CSS ( if you are using other UI libraries, please see if they are compatible, I myself would rather to use Tailwind CSS as my main CSS utility framework and use material tailwind or shading or flowbite alongside it )

then install next-themes

When you've done that, find your tailwind.config.js (in the root dictionary of the project)

Article content
here it is

In it you need to define your light and dark mode ( technically you can just make the dark mode but for customization purposes its better to do it like me, and you can control your theme right from here )

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
  theme: {
    extend: {
      colors: {

        dark: {
          bg: "#000000",
          text: "#FFFFFF",
          primary: "#27B5E2",
          secondary: "#1A97BF",
        },

        light: {
          bg: "#E6E6E6",
          text: "#000000",
          primary: "#D56C6C",
          secondary: "#EE1B1B",
        },
      },
    },
  },
  plugins: [],
  darkMode: "class",
};        

(You can chose dark mode based on user preferences via changing "class" to "media" yet I do believe it better to put dark mode as your base and add light mod as an option I mean what kind of psycho uses light mode :D, although it is dependent on the situation )

You can use the configured theme this way :

<div className="dark:bg-dark-primary bg-light-primary">
 Lett there be colours 
</div>        

As you can see it's quite easy to configure tailwind to your desired theming scheme!

Now we need a theme folder (of course naming is 99% your choice yet I find the conventional way more suitable )

Here is where you can put it

Article content
here it is

Now, in it, create 2 new files, and name them Providers and ThemeSwitcher

I'll explain what each one of them is supposed to do

for provider think of it as a context, we are going to wrap it around our main layout and the main trick of this article is here, you see when you ask or want the user to do something in your UI and use that action in anyway, it's going to be a client-side action and dark mode managing is one of them as we want user to select a theme and change our UI based on the chosen action, so by this trick we kept best of both worlds! user can choose their desired theme while the website stays SSR!

You can use this code in there (tsx)

"use client";
import React from "react";
import { ThemeProvider } from "next-themes";

const Provider: React.FC<{ children: any }> = ({ children }) => {
  return (
    <ThemeProvider attribute="class" defaultTheme="dark">
      {children}
    </ThemeProvider>
  );
};

export default Provider;

// or if you want to be fancy : 

"use client";
import React from "react";
import { ThemeProvider } from "next-themes";

const Provider = ({ children }: { children: React.ReactNode }) => {
  return (
    <ThemeProvider attribute="class" defaultTheme="dark">
      {children}
    </ThemeProvider>
  );
};

export default Provider;        

(jsx)

"use client";
import React from "react";
import { ThemeProvider } from "next-themes";

const Provider = ({ children }) => {
  return (
    <ThemeProvider attribute="class" defaultTheme="dark">
      {children}
    </ThemeProvider>
  );
};

export default Provider;        

Now just put this code in your ThemeSwitcher

import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'

const ThemeSwitch = () => {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

 
  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return null
  }

  return (
    <select value={theme} onChange={e => setTheme(e.target.value)}>
      <option value="system">System</option>
      <option value="dark">Dark</option>
      <option value="light">Light</option>
    </select>
  )
}

export default ThemeSwitch        

The code above is what next-theme has provided for us, I used headlessui to build my own toggle and styled it with tailwind, this is my code :

"use client";
import { useState, useEffect, Fragment } from "react";
import { useTheme } from "next-themes";
import { Switch } from "@headlessui/react";

const ThemeSwitch = () => {
  const [mounted, setMounted] = useState(false);
  const { theme, setTheme } = useTheme();
  const [enabled, setEnabled] = useState(true);
  // useEffect only runs on the client, so now we can safely show the UI
  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return null;
  }
  if (enabled) {
    setTheme("dark");
  } else {
    setTheme("light");
  }
  return (
    <div className="py-16">
      <Switch
        checked={enabled}
        onChange={setEnabled}
        className={`${enabled ? "bg-light-bg" : "bg-dark-bg"}
          relative inline-flex h-[38px] w-[74px] shrink-0 cursor-pointer 
          rounded-full border-2 border-transparent transition-colors 
          duration-200 ease-in-out focus:outline-none focus-visible:ring-2
            focus-visible:ring-white focus-visible:ring-opacity-75`}
      >
        <span className="sr-only">{theme}</span>
        <span
          aria-hidden="true"
          className={`${enabled ? "translate-x-9" : "translate-x-0"}
            pointer-events-none inline-block h-[34px] w-[34px] transform rounded-full
             bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
        />
      </Switch>
    </div>
  );
};
        

this is going to be your toggle , render it anywhere you want !

now lets do our finishing touches , go and wrap the provider around your main layout

like this :

return (
    <html lang="en" className="">
      <body
        className={`${inter.className} dark:bg-dark-bg dark:text-dark-text bg-light-bg text-light-text`}
      >
        <Provider>
          {children}
        </Provider>
      </body>
    </html>
  );        

and wollah, you are the proud owner of a simple dark mod that keeps your website SSR!


please do share this article and give me any recommendations you have!

let us teach others better ways to code and keep it free as others have taught us We have to teach, and keep our community beautiful and supportive as it should be <3

本間蒼士

北海道情報専門学校在学 Have an English speaking/writing skills.

2y

Thank you for the great article! Well I have a question, why are you using client detection code (about useEffect one) even you are using "use client" ? I am confusing about how "use client" should be used 😳

Like
Reply

To view or add a comment, sign in

Others also viewed

Explore content categories