Skip to content
Esc
navigateopen⌘Jpreview
On this page

Button Group

A container that groups related buttons into a segmented control.

import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';

export default function ButtonGroupDemo() {
  return (
    <ButtonGroup>
      <Button variant="outline">Archive</Button>
      <Button variant="outline">Report</Button>
      <Button variant="outline">Snooze</Button>
    </ButtonGroup>
  );
}

Install

npx shadcn@latest add @madeui/button-group

Usage

import {
  ButtonGroup,
  ButtonGroupSeparator,
  ButtonGroupText,
} from '@/components/ui/button-group';

Composition

<ButtonGroup>
  <Button />
  <Button />
</ButtonGroup>

Orientation

import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';

export default function ButtonGroupOrientation() {
  return (
    <ButtonGroup orientation="vertical">
      <Button variant="outline">Archive</Button>
      <Button variant="outline">Report</Button>
      <Button variant="outline">Snooze</Button>
    </ButtonGroup>
  );
}

Split button

Nest a DropdownMenu whose trigger renders as a ButtonButtonGroup fuses the primary action and the trigger into one control.

import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

// The trigger's `render` swaps it for a Button — ButtonGroup then fuses both
// the primary action and the trigger into one segmented control.
export default function ButtonGroupSplit() {
  return (
    <ButtonGroup>
      <Button>Deploy</Button>
      <DropdownMenu>
        <DropdownMenuTrigger render={<Button size="icon" aria-label="More deploy options" />}>
          <svg
            width="14"
            height="14"
            viewBox={`0 0 14 14`}
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
            strokeLinecap="round"
            strokeLinejoin="round"
            aria-hidden
          >
            <path d={`m3.5 5.5 3.5 3.5 3.5-3.5`} />
          </svg>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end">
          <DropdownMenuItem>Deploy to staging</DropdownMenuItem>
          <DropdownMenuItem>Roll back last deploy</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  );
}

Text

Filter
import { Button } from '@/components/ui/button';
import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText } from '@/components/ui/button-group';

export default function ButtonGroupTextExample() {
  return (
    <ButtonGroup>
      <ButtonGroupText>Filter</ButtonGroupText>
      <ButtonGroupSeparator />
      <Button variant="outline">Open</Button>
      <Button variant="outline">Closed</Button>
      <Button variant="outline">All</Button>
    </ButtonGroup>
  );
}

Input

import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';
import { Input } from '@/components/ui/input';

export default function ButtonGroupInput() {
  return (
    <ButtonGroup>
      <Input type="email" placeholder="Email address" />
      <Button variant="outline">Subscribe</Button>
    </ButtonGroup>
  );
}

API reference

ButtonGroup

Prop Type Default Description
orientation 'horizontal' | 'vertical' 'horizontal'
style StyleXStyles StyleX styles merged last — always win over the component’s own styles.

StyleX has no child selectors, so the group clones its direct children and merges position styles (end radii, collapsed inner borders) through each child’s style prop — children must be components that accept style (every component in this library does). ButtonGroupText renders a muted label segment; ButtonGroupSeparator divides segments of a filled group.

Was this page helpful?