Use Theme

Extend next-theme's use-theme hook to manage multiple shadcn color themes.

Preview

Installation

CLI

npx shadcn@latest add https://kelvinmai.io/r/theme-switch.json

Manual

Install the following dependencies

npm install next-themes

Set up next-themes in your Next.js app

app/layout.tsx
import { ThemeProvider } from 'next-themes';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<ThemeProvider attribute='class' defaultTheme='system' enableSystem>
{children}
</ThemeProvider>
);
}

To develop your own themes you can get some inspiration or customize your own from tweakcn.

Copy and paste the following code into your project

hooks/use-theme.ts
'use client';
import * as React from 'react';
import { useTheme as useNextTheme } from 'next-themes';
export const colorThemes = [
Add your own theme names here
'default',
'bubblegum',
'monochrome',
'supabase',
'twitter',
'vercel',
] as const;
export type ColorTheme = (typeof colorThemes)[number];
export type Theme = 'system' | 'light' | 'dark';
export const useTheme = () => {
const { setTheme, resolvedTheme } = useNextTheme();
const [colorTheme, setColorTheme] = React.useState<ColorTheme>('default');
React.useEffect(() => {
if (typeof window === 'undefined') return;
const saved = localStorage.getItem('color-theme') as ColorTheme | null;
if (saved) {
setColorTheme(saved);
document.documentElement.classList.add(`theme-${saved}`);
}
}, []);
const updateColorTheme = React.useCallback((next: ColorTheme) => {
if (typeof window === 'undefined') return;
document.documentElement.classList.remove(
...colorThemes.map((c) => `theme-${c}`),
);
if (next !== 'default') {
document.documentElement.classList.add(`theme-${next}`);
}
localStorage.setItem('color-theme', next);
setColorTheme(next);
}, []);
return {
setTheme,
theme: resolvedTheme as Theme,
colorTheme: colorTheme,
setColorTheme: updateColorTheme,
};
};

Update the import paths to match your project setup

Usage

Add multiple custom themes to your global css file

app/globals.css
.theme-yours {
By default this hook assumes theme names are prefixed with `theme-`
/* ... */
}
.theme-yours.dark {
/* ... */
}
import { useTheme } from '@/hooks/use-theme';
const { theme, setTheme, colorTheme, setColorTheme } = useTheme();

API Reference

Returns

The useToggle hook returns an object with the following elements:

NameTypeDescription
theme
Theme
The resolved theme provided by next-themes.
setTheme
(Theme) => void
Function to set the next-theme theme.
colorTheme
ColorTheme
The current theme of your pre-defined shadcn color theme.
setColorTheme
(ColorTheme) => void
Function to set the the color theme.