HapplyUI

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-tabs
  • merge-refs

Registry dependencies

These are automatically installed when you add this component.

  • use-tab-observer
  • happly-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.

PropTypeDefaultDescription
defaultValuestring-The default active tab value (uncontrolled)
valuestring-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.

PropTypeDefaultDescription
floatingBgClassNamestring-Additional classes for the floating background indicator element

SwitchToggle.Trigger

An individual tab trigger button.

PropTypeDefaultDescription
valuestring-The unique value for this tab trigger
disabledbooleanfalseDisables the trigger

SwitchToggle.Content

The content panel displayed when its corresponding trigger is active.

PropTypeDefaultDescription
valuestring-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.

PropTypeDefaultDescription
itemsSwitchToggleGroupItem[]-Array of items with value, label, and optional icon
listClassNamestring-Additional classes for the List container
floatingBgClassNamestring-Additional classes for the floating background indicator
defaultValuestring-The default active tab value (uncontrolled)
valuestring-The controlled active tab value
onValueChange(value: string) => void-Callback when the active tab changes

Previous
Accordion