Theme Toggle Button
Button with morphing SVG animation between Sun and Moon icons using GSAP
Getting Started
Install dependencies
npm install next-themes gsapAdd the component
npx shadcn@latest add https://glsilk.vercel.app/r/theme-toggle-button.jsonCreate a theme provider
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}Wrap your root layout
import { ThemeProvider } from "@/components/theme-provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
</body>
</html>
);
}Create a reusable mode toggle component
"use client";
import { useTheme } from "next-themes";
import { ThemeToggleButton } from "@/components/ui/theme-toggle-button";
export function ModeToggle() {
const { theme } = useTheme();
return (
<div className="flex flex-col items-center gap-4">
<ThemeToggleButton variant="ghost" size="icon" />
</div>
);
}Examples
Default
Open in<ThemeToggleButton>Toggle Theme</ThemeToggleButton>Rounded
Open in<div className="flex gap-4">
<ThemeToggleButton variant="outline" size="icon" className="rounded-full" />
<ThemeToggleButton variant="default" size="icon" className="rounded-full" />
<ThemeToggleButton variant="destructive" size="icon" className="rounded-full" />
</div>
<div className="flex gap-4">
<ThemeToggleButton
variant="outline"
size="icon"
className="rounded-full [&_svg_circle]:fill-yellow-500 [&_svg_path]:stroke-yellow-500 [&[data-state=checked]_svg_circle]:fill-blue-500 [&[data-state=checked]_svg_path]:stroke-blue-500"
/>
<ThemeToggleButton
variant="outline"
size="icon"
className="rounded-full [&_svg_circle]:fill-orange-500 [&_svg_path]:stroke-orange-500 [&[data-state=checked]_svg_circle]:fill-purple-500 [&[data-state=checked]_svg_path]:stroke-purple-500"
/>
<ThemeToggleButton
variant="outline"
size="icon"
className="rounded-full [&_svg_circle]:fill-green-500 [&_svg_path]:stroke-green-500 [&[data-state=checked]_svg_circle]:fill-pink-500 [&[data-state=checked]_svg_path]:stroke-pink-500"
/>
</div>Custom Colors
Open in<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-yellow-500 [&_svg_path]:stroke-yellow-500"
/>
<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-blue-500 [&_svg_path]:stroke-blue-500"
/>
<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-orange-500 [&_svg_path]:stroke-orange-500"
/>
<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-purple-500 [&_svg_path]:stroke-purple-500"
/>Dual Color Mode
Open in<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-yellow-500 [&_svg_path]:stroke-yellow-500 [&[data-state=checked]_svg_circle]:fill-blue-500 [&[data-state=checked]_svg_path]:stroke-blue-500"
/>
<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-orange-500 [&_svg_path]:stroke-orange-500 [&[data-state=checked]_svg_circle]:fill-purple-500 [&[data-state=checked]_svg_path]:stroke-purple-500"
/>
<ThemeToggleButton
size="icon"
variant="outline"
className="[&_svg_circle]:fill-green-500 [&_svg_path]:stroke-green-500 [&[data-state=checked]_svg_circle]:fill-pink-500 [&[data-state=checked]_svg_path]:stroke-pink-500"
/>API Reference
Prop
Type
Credits
- GSAP: greensock.com
- Icons: lucide-react