Theme Button

Provides an elegant and user-friendly way to cycle through light, dark, and system themes. It's specifically designed to work with next-themes, the most popular theme management library for Next.js applications.

npx shadcn@latest add https://shuip.plvo.dev/r/theme-button.json
pnpm dlx shadcn@latest add https://shuip.plvo.dev/r/theme-button.json
bun x shadcn@latest add https://shuip.plvo.dev/r/theme-button.json
import type { VariantProps } from 'class-variance-authority';
import { Laptop, Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import * as React from 'react';
import { Button, type buttonVariants } from '@/components/ui/button';
type ButtonProps = React.ComponentProps<'button'> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
};
type Theme = 'system' | 'light' | 'dark';
export interface ThemeButtonProps extends ButtonProps {
withText?: boolean;
}
export function ThemeButton({ withText, ...props }: ThemeButtonProps) {
const { theme, setTheme } = useTheme();
const [currentTheme, setCurrentTheme] = React.useState<Theme>('system');
React.useEffect(() => {
setCurrentTheme(theme as Theme);
}, [theme]);
const cycleTheme = () => {
const themes: Theme[] = ['system', 'light', 'dark'];
const currentIndex = themes.indexOf(currentTheme);
const nextIndex = (currentIndex + 1) % themes.length;
setTheme(themes[nextIndex]);
};
const getThemeIcon = () => {
switch (currentTheme) {
case 'system':
return <Laptop className='size-[1.2rem]' />;
case 'light':
return <Sun className='size-[1.2rem]' />;
case 'dark':
return <Moon className='size-[1.2rem]' />;
}
};
const getThemeText = () => {
return currentTheme.charAt(0).toUpperCase() + currentTheme.slice(1);
};
return (
<Button variant='outline' size={withText ? 'default' : 'icon'} onClick={cycleTheme} {...props}>
{getThemeIcon()}
{withText && <span className='ml-2 capitalize'>{getThemeText()}</span>}
<span className='sr-only'>Toggle theme</span>
</Button>
);
}
Loading...

Ready-to-use theme toggle

The button automatically cycles through these states:

  • System: Uses the user's OS preference (🖥️ Laptop icon)
  • Light: Forces light mode (☀️ Sun icon)
  • Dark: Forces dark mode (🌙 Moon icon)

With ThemeButton, a complete theme switcher in one line:

With ThemeButton - simple and complete

<ThemeButton />

With text label

<ThemeButton withText={true} />

Setup with next-themes

First, make sure you have next-themes installed and configured:

Examples

Default

Loading...
import { ThemeButton } from '@/components/ui/shuip/theme-button';
export default function ThemeButtonExample() {
return <ThemeButton />;
}

Text

Loading...
import { ThemeButton } from '@/components/ui/shuip/theme-button';
export default function ThemeButtonTextExample() {
return <ThemeButton withText />;
}

Props

Prop

Type

On this page