Toggle Buttons

A set of buttons where only one can be selected at a time, with smooth animated transitions between selections.

Props

ToggleButtons

PropTypeDefaultDescription
valuestring-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
paddingbooleantrueWhether to add padding between container and buttons
classNamestring-Custom CSS classes
childrenReactNode-ToggleButton components

ToggleButton

PropTypeDefaultDescription
valuestring-Value for this button
iconbooleanfalseIf true, renders as square icon button with proper sizing
asChildbooleanfalseRender as child element (using Slot)
classNamestring-Custom CSS classes
childrenReactNode-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:

Selected: home

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>
);