Demo
https://next13-dark.vercel.app
The version of Next.js is 13.4.10.
Conclusion
- Saving and reading the current theme using the cookies
 - The theme change using the Server Actions
 - Thematic styles can be set with CSS Modules or Tailwind CSS
 
  
  
  Step 1: Enable Server Actions
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    serverActions: true // Add,
  },
};
module.exports = nextConfig;
  
  
  Step 2: Set the current theme to html tag
// layout.tsx
import { cookies } from "next/headers";
import ThemeToggle from "./_components/themeToggle"
export default function RootLayout({ children }: { children: React.ReactNode }) {
  const defaultTheme = "light";
  const cookieValue = cookies().get("theme")?.value || "";
  const isTheme = cookieValue === defaultTheme || cookieValue === "dark";
  const theme = isTheme ? cookieValue : defaultTheme;
  return (
    <html className={theme}>
      <body>
        <ThemeToggle currentTheme={theme} />
        {children}
      </body>
    </html>
  );
}
Step 3: Create a button to switch themes
// themeToggle.tsx
"use client"; // Required to set the onClick.
import { setTheme } from "./action";
export default function ThemeToggle({ currentTheme }: { currentTheme: string }) {
  const changeTheme = () => setTheme(currentTheme);
  return (
    <button onClick={changeTheme}>
      {currentTheme === "light" ? "Dark" : "Light"}
    </button>
  );
}
  
  
  Step 4: Create a Server Actions to change the theme
"use server";
import { cookies } from "next/headers";
import { revalidatePath } from "next/cache";
export const setTheme = (currentTheme = "") => {
  const newTheme = currentTheme === "light" ? "dark" : "light";
  cookies().set("theme", newTheme); // This method can only be used with `Server Actions` or `Route Handlers`.
  revalidatePath("/");
};
Step 5: Change styles by theme
// globals.scss
html {
  background: white;
  color: black;
  &.dark {
    background: black;
    color: white;
  }
}
a {
  color: steelblue;
  html.dark & {
    color: skyblue;
  }
}
For Scss Modules.
// page.module.scss
.wrap {
  background: white;
  color: black;
}
:global(html.dark) {
  .wrap {
    background: black;
    color: white;
  }
}
              
    
Top comments (2)
This is awesome! Exactly what I was looking for; there shouldn't be a need to client side render so many components just to support a stateful darkmode.
Thank you