Buttons joined into one segmented control — horizontal, vertical or split.

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

export default function ButtonGroupDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-6">
      <ButtonGroup>
        <Button variant="outline">Archive</Button>
        <Button variant="outline">Report</Button>
        <Button variant="outline">Snooze</Button>
      </ButtonGroup>

      <ButtonGroup>
        <Button variant="secondary">Save</Button>
        <ButtonGroupSeparator />
        <Button variant="secondary">Save as…</Button>
      </ButtonGroup>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/button-group.json

Needs a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.

Button Group imports cn() from lib/utils.ts — add it too if you don't have it yet:

import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Usage

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

<ButtonGroup>
  <Button variant="outline">Left</Button>
  <Button variant="outline">Middle</Button>
  <Button variant="outline">Right</Button>
</ButtonGroup>

Examples

Vertical

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

export default function ButtonGroupVertical() {
  return (
    <div className="flex items-center gap-6">
      <ButtonGroup orientation="vertical" aria-label="Zoom controls">
        <Button variant="outline" size="icon" aria-label="Zoom in">
          <PlusIcon />
        </Button>
        <Button variant="outline" size="icon" aria-label="Zoom out">
          <MinusIcon />
        </Button>
      </ButtonGroup>

      <ButtonGroup orientation="vertical" aria-label="View">
        <Button variant="outline">Day</Button>
        <Button variant="outline">Week</Button>
        <Button variant="outline">Month</Button>
      </ButtonGroup>
    </div>
  )
}

function PlusIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 12h14M12 5v14" />
    </svg>
  )
}

function MinusIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 12h14" />
    </svg>
  )
}

Split button

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

/** A split button: the main action, and a menu of alternatives beside it. */
export default function ButtonGroupSplit() {
  return (
    <ButtonGroup>
      <Button variant="secondary">Publish</Button>
      <ButtonGroupSeparator />
      <DropdownMenu>
        <DropdownMenuTrigger
          render={
            <Button variant="secondary" size="icon" aria-label="More publish options">
              <ChevronDownIcon />
            </Button>
          }
        />
        <DropdownMenuContent align="end" className="min-w-44">
          <DropdownMenuItem>Publish now</DropdownMenuItem>
          <DropdownMenuItem>Schedule…</DropdownMenuItem>
          <DropdownMenuSeparator />
          <DropdownMenuItem>Save as draft</DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  )
}

function ChevronDownIcon() {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
      <path d="m6 9 6 6 6-6" />
    </svg>
  )
}

API Reference

NameTypeDefaultDescription
ButtonGroup orientation'horizontal' | 'vertical''horizontal'Direction of the group. Only the outer corners stay rounded.
ButtonGroupSeparatorcomponent—A divider between buttons that share a fill (default, secondary); outline buttons already have a border.
ButtonGroupTextcomponent—A non-interactive label attached to the group, such as a count.