diff --git a/package.json b/package.json
index 7c5f3e8..5f76957 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"drizzle-zod": "^0.8.3",
"lucide-react": "^0.544.0",
"next": "15.4.6",
+ "next-themes": "^0.4.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-hook-form": "^7.62.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 11a3696..b14f415 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,6 +62,9 @@ importers:
next:
specifier: 15.4.6
version: 15.4.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ next-themes:
+ specifier: ^0.4.6
+ version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react:
specifier: 19.1.0
version: 19.1.0
@@ -3220,6 +3223,12 @@ packages:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
+ next-themes@0.4.6:
+ resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
next@15.4.6:
resolution: {integrity: sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
@@ -7423,6 +7432,11 @@ snapshots:
negotiator@1.0.0: {}
+ next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
next@15.4.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.4.6
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c4406ed..6ae61e4 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Toaster } from "react-hot-toast";
+import { ThemeProvider } from "@/components/theme-provider";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -27,12 +28,19 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
- {children}
-
+
+ {children}
+
+
);
diff --git a/src/components/mode-toggle.tsx b/src/components/mode-toggle.tsx
new file mode 100644
index 0000000..eb4d819
--- /dev/null
+++ b/src/components/mode-toggle.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { Moon, Sun, Monitor } from "lucide-react";
+import { useTheme } from "next-themes";
+import { useEffect, useState } from "react";
+import { Button } from "@/components/ui/button";
+
+export function ModeToggle() {
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = useState(false);
+
+ // Avoid hydration mismatch by only rendering after mount
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) {
+ return (
+
+ );
+ }
+
+ const cycleTheme = () => {
+ if (theme === "light") {
+ setTheme("dark");
+ } else if (theme === "dark") {
+ setTheme("system");
+ } else {
+ setTheme("light");
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx
index a6a3b0f..04535c3 100644
--- a/src/components/navigation.tsx
+++ b/src/components/navigation.tsx
@@ -2,16 +2,17 @@ import { CheckSquare, Home } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import LogoutButton from "../modules/auth/components/logout-button";
+import { ModeToggle } from "@/components/mode-toggle";
export function Navigation() {
return (
-