Theme Toggle Effect
Animated transitions when switching between light and dark themes.
Browser compatibility
This component uses the View Transitions API. Check the latest browser compatibility on MDN before using it in production. Browsers without the API, and users who prefer reduced motion, switch themes instantly.
Installation
Pick one effect. Each command installs the helper and that effect's CSS.
Triangle

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-triangleTriangle blur

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-triangle-blurCircle

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-circleCircle blur

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-circle-blurCircle blur (top left)

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-circle-blur-top-leftPolygon

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-polygonPolygon gradient

pnpm dlx shadcn@latest add @cortexcn/theme-toggle-effect-polygon-gradientUsage
Wrap your theme setter with startThemeTransition and pass the effect you installed:
"use client";
import { useTheme } from "next-themes";
import { startThemeTransition } from "@/components/theme-toggle-effect";
export function ThemeToggle() {
const { resolvedTheme, setTheme } = useTheme();
function toggleTheme() {
const next = resolvedTheme === "dark" ? "light" : "dark";
startThemeTransition(() => setTheme(next), "circle");
}
return <button onClick={toggleTheme}>Toggle theme</button>;
}It works with any theme setter, not only next-themes:
startThemeTransition(() => {
document.documentElement.classList.toggle("dark");
}, "polygon");The preview above has every effect installed so you can compare them. In your app, install the effect you use and pass the same name to startThemeTransition.
startThemeTransition sets data-theme-toggle-effect on <html> for the length of the switch, and every effect rule is scoped to that attribute. The effect CSS never runs for other view transitions, such as page navigations.
API
function startThemeTransition(
update: () => void,
effect?: ThemeToggleEffect,
): void;| Parameter | Type | Default | Description |
|---|---|---|---|
update | () => void | required | Applies the new theme, usually setTheme |
effect | ThemeToggleEffect | "circle" | Which reveal effect to play |
THEME_TOGGLE_EFFECTS is a readonly array of every effect name, and ThemeToggleEffect is the union of its values.
