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