Openstatus
www.openstatus.dev
1"use client";
2
3import { useTheme } from "next-themes";
4import type * as React from "react";
5
6import {
7 Select,
8 SelectContent,
9 SelectItem,
10 SelectTrigger,
11 SelectValue,
12} from "@/components/ui/select";
13import { cn } from "@/lib/utils";
14import { Laptop, Moon, Sun } from "lucide-react";
15import { useState } from "react";
16import { useEffect } from "react";
17
18export function ThemeToggle({
19 className,
20 ...props
21}: React.ComponentProps<typeof SelectTrigger>) {
22 const { setTheme, theme } = useTheme();
23 const [mounted, setMounted] = useState(false);
24
25 useEffect(() => {
26 setMounted(true);
27 }, []);
28
29 // NOTE: hydration error if we don't do this
30 if (!mounted) {
31 return (
32 <Select>
33 <SelectTrigger className={cn("w-[180px]", className)} {...props}>
34 <SelectValue placeholder="Select theme" />
35 </SelectTrigger>
36 </Select>
37 );
38 }
39
40 return (
41 <Select value={theme} onValueChange={setTheme}>
42 <SelectTrigger className={cn("w-[180px]", className)} {...props}>
43 <SelectValue defaultValue={theme} placeholder="Select theme" />
44 </SelectTrigger>
45 <SelectContent>
46 <SelectItem value="light">
47 <div className="flex items-center gap-2">
48 <Sun className="h-4 w-4" />
49 <span>Light</span>
50 </div>
51 </SelectItem>
52 <SelectItem value="dark">
53 <div className="flex items-center gap-2">
54 <Moon className="h-4 w-4" />
55 <span>Dark</span>
56 </div>
57 </SelectItem>
58 <SelectItem value="system">
59 <div className="flex items-center gap-2">
60 <Laptop className="h-4 w-4" />
61 <span>System</span>
62 </div>
63 </SelectItem>
64 </SelectContent>
65 </Select>
66 );
67}