> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BankkRoll/tweakcn-theme-picker/llms.txt
> Use this file to discover all available pages before exploring further.

# ThemeSwitcher

> Dropdown UI component for selecting color themes and toggling light/dark mode

## ThemeSwitcher

A dropdown menu component that allows users to switch between color themes and toggle between light/dark modes. Built with shadcn/ui components and fully integrated with next-themes.

<Note>
  Located in `registry/nextjs/components/theme-switcher.tsx`
</Note>

## Component Signature

```typescript theme={null}
export function ThemeSwitcher(): JSX.Element
```

## Props

This component accepts no props. All configuration is handled internally through the `useTheme` hook.

## Features

* **Dual Theme Controls**: Separate controls for color theme and light/dark mode
* **Visual Theme Previews**: Color dots showing each theme's primary color
* **Current Theme Indicator**: Check marks on active selections
* **Scrollable Theme List**: Smooth scrolling for 45+ themes
* **SSR Safe**: Prevents hydration mismatches with mounted state
* **Keyboard Accessible**: Full keyboard navigation support

## Usage

```tsx theme={null}
import { ThemeSwitcher } from "@/components/theme-switcher";

export default function Header() {
  return (
    <header>
      <nav>
        <h1>My App</h1>
        <ThemeSwitcher />
      </nav>
    </header>
  );
}
```

## UI Structure

### Trigger Button

<ResponseField name="Button" type="Component">
  Ghost variant icon button (9×9) displaying Sun (light mode) or Moon (dark mode) icon.

  ```tsx theme={null}
  <Button variant="ghost" size="icon" className="h-9 w-9">
    {isDark ? <Moon className="h-4 w-4" /> : <Sun className="h-4 w-4" />}
  </Button>
  ```
</ResponseField>

### Dropdown Content

The dropdown menu contains three main sections:

#### 1. Current Theme Label

<ResponseField name="Label" type="DropdownMenuLabel">
  Shows the current theme name with a colored preview dot.

  ```tsx theme={null}
  <DropdownMenuLabel className="flex items-center gap-2">
    <div
      className="h-3 w-3 rounded-full"
      style={{ backgroundColor: isDark ? primaryDark : primaryLight }}
    />
    {currentThemeConfig.title}
  </DropdownMenuLabel>
  ```
</ResponseField>

#### 2. Mode Toggle (Light/Dark)

<ResponseField name="Mode Items" type="DropdownMenuItem[]">
  Two items for switching between light and dark modes. Active mode shows a check mark.

  ```tsx theme={null}
  <DropdownMenuItem onClick={() => setMode("light")}>
    <Sun className="h-4 w-4" />
    <span>Light</span>
    {mode === "light" && <Check className="h-4 w-4" />}
  </DropdownMenuItem>
  ```
</ResponseField>

#### 3. Color Theme Submenu

<ResponseField name="Theme Submenu" type="DropdownMenuSub">
  Scrollable submenu with all 45 themes, each showing a color preview.

  ```tsx theme={null}
  <DropdownMenuSub>
    <DropdownMenuSubTrigger>
      <Palette className="h-4 w-4" />
      <span>Color Theme</span>
    </DropdownMenuSubTrigger>
    <DropdownMenuSubContent>
      <ScrollArea className="h-80">
        {sortedThemes.map((theme) => (
          <DropdownMenuItem onClick={() => setColorTheme(theme.name)}>
            <div className="h-4 w-4 rounded-full" style={{ backgroundColor }} />
            <span>{theme.title}</span>
            {isActive && <Check className="h-4 w-4" />}
          </DropdownMenuItem>
        ))}
      </ScrollArea>
    </DropdownMenuSubContent>
  </DropdownMenuSub>
  ```
</ResponseField>

## Internal Functions

### parseTheme

Helper function to extract color theme and mode from theme string.

```typescript theme={null}
function parseTheme(theme: string | undefined): {
  colorTheme: string;
  mode: "light" | "dark";
}
```

<ParamField path="theme" type="string | undefined">
  Theme string like `"catppuccin-dark"` or `"vercel-light"`
</ParamField>

**Returns:**

<ResponseField name="colorTheme" type="string">
  Theme name without mode suffix (e.g., `"catppuccin"`)
</ResponseField>

<ResponseField name="mode" type="'light' | 'dark'">
  Extracted mode from theme string, defaults to `"dark"`
</ResponseField>

**Examples:**

```typescript theme={null}
parseTheme("catppuccin-dark")  // { colorTheme: "catppuccin", mode: "dark" }
parseTheme("vercel-light")     // { colorTheme: "vercel", mode: "light" }
parseTheme(undefined)          // { colorTheme: "default", mode: "dark" }
```

### setColorTheme

Sets the color theme while preserving the current mode.

```typescript theme={null}
const setColorTheme = (name: string) => {
  setTheme(`${name}-${mode}`);
};
```

**Example:**

```typescript theme={null}
// Current: "default-dark"
setColorTheme("catppuccin");
// Result: "catppuccin-dark"
```

### setMode

Sets the light/dark mode while preserving the current color theme.

```typescript theme={null}
const setMode = (newMode: "light" | "dark") => {
  setTheme(`${colorTheme}-${newMode}`);
};
```

**Example:**

```typescript theme={null}
// Current: "catppuccin-dark"
setMode("light");
// Result: "catppuccin-light"
```

## Hydration Safety

The component uses a `mounted` state to prevent hydration mismatches:

```typescript theme={null}
const [mounted, setMounted] = useState(false);

useEffect(() => {
  setMounted(true);
}, []);

if (!mounted) {
  return (
    <Button variant="ghost" size="icon" className="h-9 w-9">
      <Sun className="h-4 w-4" />
    </Button>
  );
}
```

This ensures the server-rendered HTML matches the initial client render before showing the actual theme state.

## Dependencies

### Required Components (shadcn/ui)

* `Button` - Trigger button component
* `DropdownMenu` - Main menu container
* `DropdownMenuContent` - Menu content wrapper
* `DropdownMenuItem` - Individual menu items
* `DropdownMenuLabel` - Menu section labels
* `DropdownMenuSeparator` - Visual dividers
* `DropdownMenuSub` - Nested submenu
* `DropdownMenuSubTrigger` - Submenu trigger
* `DropdownMenuSubContent` - Submenu content
* `DropdownMenuPortal` - Portal for submenu rendering
* `ScrollArea` - Scrollable theme list

### Required Icons (lucide-react)

* `Sun` - Light mode icon
* `Moon` - Dark mode icon
* `Palette` - Theme picker icon
* `Check` - Active selection indicator

### Required Utilities

* `useTheme` from `next-themes` - Theme state management
* `cn` from `@/lib/utils` - Class name utility
* `sortedThemes` from `@/lib/themes-config` - Sorted theme list
* `themes` from `@/lib/themes-config` - Theme configurations

## Styling

The component uses Tailwind CSS classes and is fully customizable:

```tsx theme={null}
// Trigger button
<Button variant="ghost" size="icon" className="h-9 w-9">

// Dropdown content
<DropdownMenuContent align="end" className="w-48">

// Theme color preview
<div className="h-4 w-4 rounded-full border border-border" />

// Scrollable area
<ScrollArea className="h-80">
```

## Accessibility

* **Screen Reader Support**: Trigger button includes `<span class="sr-only">Toggle theme</span>`
* **Keyboard Navigation**: Full keyboard support via DropdownMenu
* **Visual Indicators**: Check marks for active selections
* **Color Previews**: Visual dots help users identify themes

## Advanced Customization

### Custom Button Styling

Wrap and style the component:

```tsx theme={null}
<div className="rounded-lg border p-1">
  <ThemeSwitcher />
</div>
```

### Fork for Custom Behavior

Copy and modify the component to:

* Filter visible themes
* Add custom theme groupings
* Change icon sets
* Modify menu structure

```tsx theme-switcher-custom.tsx theme={null}
import { ThemeSwitcher } from "@/components/theme-switcher";

// Filter to specific themes
const filteredThemes = sortedThemes.filter(t => 
  ["default", "catppuccin", "vercel"].includes(t.name)
);
```

## See Also

* [useTheme](/api/use-theme) - Theme state hook
* [ThemeProvider](/api/theme-provider) - Provider setup
* [sortedThemes](/api/themes-config-exports#sortedthemes) - Sorted theme list
* [ThemeConfig](/api/theme-config) - Theme object structure
