Components
Switch Toggle
A tab-based switch toggle with an animated floating background indicator. Built on Radix Tabs for full accessibility.
Installation
bunx @happlyui/cli@latest add switch-toggle
Dependencies
npm packages
@radix-ui/react-slot@radix-ui/react-tabsmerge-refs
Registry dependencies
These are automatically installed when you add this component.
use-tab-observerhapply-ui-utils
Usage
import * as SwitchToggle from "@/components/ui/switch-toggle"
<SwitchToggle.Root defaultValue="option1">
<SwitchToggle.List>
<SwitchToggle.Trigger value="option1">Option 1</SwitchToggle.Trigger>
<SwitchToggle.Trigger value="option2">Option 2</SwitchToggle.Trigger>
</SwitchToggle.List>
<SwitchToggle.Content value="option1">Content 1</SwitchToggle.Content>
<SwitchToggle.Content value="option2">Content 2</SwitchToggle.Content>
</SwitchToggle.Root>
Examples
Default
Rounded switch toggle with icons and text labels for a theme switcher.
<SwitchToggle.Root defaultValue='system'>
<SwitchToggle.List>
<SwitchToggle.Trigger value='light'>
<RiSunLine className='w-5 h-5 shrink-0' />
Light
</SwitchToggle.Trigger>
<SwitchToggle.Trigger value='dark'>
<RiMoonLine className='w-5 h-5 shrink-0' />
Dark
</SwitchToggle.Trigger>
<SwitchToggle.Trigger value='system'>
<RiEqualizer3Fill className='w-5 h-5 shrink-0' />
System
</SwitchToggle.Trigger>
</SwitchToggle.List>
</SwitchToggle.Root>
Icon Only (Rounded)
Compact rounded variant with icon-only triggers, ideal for tight layouts.
<SwitchToggle.Root defaultValue='system'>
<SwitchToggle.List className='w-fit gap-2 rounded-full' floatingBgClassName='rounded-full'>
<SwitchToggle.Trigger value='light' className='aspect-square h-9'>
<RiSunLine className='w-6 h-6' />
</SwitchToggle.Trigger>
<SwitchToggle.Trigger value='dark' className='aspect-square h-9'>
<RiMoonLine className='w-6 h-6' />
</SwitchToggle.Trigger>
<SwitchToggle.Trigger value='system' className='aspect-square h-9'>
<RiEqualizer3Fill className='w-6 h-6' />
</SwitchToggle.Trigger>
</SwitchToggle.List>
</SwitchToggle.Root>
Group (Composed)
Composed group component with items as props, including icons.
const items = [
{ value: 'light', label: 'Light', icon: RiSunLine },
{ value: 'dark', label: 'Dark', icon: RiMoonLine },
{ value: 'system', label: 'System', icon: RiEqualizer3Fill },
];
<SwitchToggle.Group defaultValue='system' items={items} />
Group Text Only
Composed group with text-only items, no icons.
const items = [
{ value: 'daily', label: 'Daily' },
{ value: 'weekly', label: 'Weekly' },
{ value: 'monthly', label: 'Monthly' },
];
<SwitchToggle.Group defaultValue='weekly' items={items} />
API Reference
SwitchToggle.Root
The root container. Wraps Radix Tabs.Root.
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | string | - | The default active tab value (uncontrolled) |
value | string | - | The controlled active tab value |
onValueChange | (value: string) => void | - | Callback when the active tab changes |
SwitchToggle.List
The tab list container with animated floating background indicator.
| Prop | Type | Default | Description |
|---|---|---|---|
floatingBgClassName | string | - | Additional classes for the floating background indicator element |
SwitchToggle.Trigger
An individual tab trigger button.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The unique value for this tab trigger |
disabled | boolean | false | Disables the trigger |
SwitchToggle.Content
The content panel displayed when its corresponding trigger is active.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The value matching the trigger this content belongs to |
SwitchToggle.Group
Composed switch toggle that renders triggers from an items array. Handles icon rendering automatically.
| Prop | Type | Default | Description |
|---|---|---|---|
items | SwitchToggleGroupItem[] | - | Array of items with value, label, and optional icon |
listClassName | string | - | Additional classes for the List container |
floatingBgClassName | string | - | Additional classes for the floating background indicator |
defaultValue | string | - | The default active tab value (uncontrolled) |
value | string | - | The controlled active tab value |
onValueChange | (value: string) => void | - | Callback when the active tab changes |