Toggle Buttons
A set of buttons where only one can be selected at a time, with smooth animated transitions between selections.
Props
ToggleButtons
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Controlled value |
onValueChange | (value: string) => void | - | Callback when value changes |
size | 'sm' | 'base' | 'lg' | 'base' | Size of the buttons |
variant | 'primary' | 'secondary' | 'ghost' | 'secondary' | Visual style variant |
padding | boolean | true | Whether to add padding between container and buttons |
className | string | - | Custom CSS classes |
children | ReactNode | - | ToggleButton components |
ToggleButton
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Value for this button |
icon | boolean | false | If true, renders as square icon button with proper sizing |
asChild | boolean | false | Render as child element (using Slot) |
className | string | - | Custom CSS classes |
children | ReactNode | - | Button content |
Icon Only
Perfect for toolbar-style interfaces with square buttons:
Primary
Secondary
Ghost
Selected: select
import { MousePointer, Hand, Scissors } from "lucide-react";
const [selectedTool, setSelectedTool] = useState("select");
return (
<div className="space-y-6">
<div className="space-y-2">
<p className="text-sm font-medium">Primary</p>
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool} padding={false} variant="primary">
<ToggleButton value="select" icon>
<MousePointer className="size-4" />
</ToggleButton>
<ToggleButton value="pick" icon>
<Hand className="size-4" />
</ToggleButton>
<ToggleButton value="cut" icon>
<Scissors className="size-4" />
</ToggleButton>
</ToggleButtons>
</div>
<div className="space-y-2">
<p className="text-sm font-medium">Secondary</p>
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool} padding={false} variant="secondary">
<ToggleButton value="select" icon>
<MousePointer className="size-4" />
</ToggleButton>
<ToggleButton value="pick" icon>
<Hand className="size-4" />
</ToggleButton>
<ToggleButton value="cut" icon>
<Scissors className="size-4" />
</ToggleButton>
</ToggleButtons>
</div>
<div className="space-y-2">
<p className="text-sm font-medium">Ghost</p>
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool} padding={false} variant="ghost">
<ToggleButton value="select" icon>
<MousePointer className="size-4" />
</ToggleButton>
<ToggleButton value="pick" icon>
<Hand className="size-4" />
</ToggleButton>
<ToggleButton value="cut" icon>
<Scissors className="size-4" />
</ToggleButton>
</ToggleButtons>
</div>
</div>
);
Usage
Selected: select
const [selectedTool, setSelectedTool] = useState("select");
return (
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool}>
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
);
Padding Options
Control the spacing between the container and buttons:
With Padding (default)
Without Padding
Selected: select
const [selectedTool, setSelectedTool] = useState("select");
return (
<>
{/* With padding (default) */}
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool} padding={true} variant="primary">
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
{/* Without padding - buttons inherit full container size */}
<ToggleButtons value={selectedTool} onValueChange={setSelectedTool} padding={false} variant="primary">
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
</>
);
Sizes
Small
Base
Large
Selected: select
const [selectedTool, setSelectedTool] = useState("select");
return (
<>
<ToggleButtons size="sm" value={selectedTool} onValueChange={setSelectedTool}>
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
<ToggleButtons size="base" value={selectedTool} onValueChange={setSelectedTool}>
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
<ToggleButtons size="lg" value={selectedTool} onValueChange={setSelectedTool}>
<ToggleButton value="select">Select</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
</>
);
Custom Styling
Selected: select
const [selectedTool, setSelectedTool] = useState("select");
return (
<ToggleButtons
value={selectedTool}
onValueChange={setSelectedTool}
className="bg-blue-100 border-2 border-blue-300" // Custom container styles
>
<ToggleButton
value="select"
className="font-bold text-blue-600" // Custom item styles
>
Select
</ToggleButton>
<ToggleButton value="pick">Pick</ToggleButton>
<ToggleButton value="cut">Cut</ToggleButton>
</ToggleButtons>
);
AsChild
Use asChild to render the toggle button as a different element while maintaining all functionality:
const [selectedPage, setSelectedPage] = useState("home");
return (
<ToggleButtons value={selectedPage} onValueChange={setSelectedPage}>
<ToggleButton value="home" asChild>
<a href="#home">Home</a>
</ToggleButton>
<ToggleButton value="about" asChild>
<a href="#about">About</a>
</ToggleButton>
<ToggleButton value="contact" asChild>
<a href="#contact">Contact</a>
</ToggleButton>
</ToggleButtons>
);