Embossed UI

Search documentation

Search for a page or component.

Button Group

A container that groups related buttons together with consistent styling.

"use client"

import { ArrowLeftIcon } from "lucide-react"

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

export function ButtonGroupDemo() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <ButtonGroup>
        <Button variant="secondary" size="icon" aria-label="Go back">
          <ArrowLeftIcon />
        </Button>
        <Button variant="secondary">Archive</Button>
        <Button variant="secondary">Report</Button>
        <Button variant="secondary">Snooze</Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Day</Button>
        <Button variant="outline">Week</Button>
        <Button variant="outline">Month</Button>
      </ButtonGroup>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add https://ui.ikramhasan.com/r/button-group.json

Usage

import {
  ButtonGroup,
  ButtonGroupSeparator,
  ButtonGroupText,
} from "@/components/ui/button-group"
<ButtonGroup>
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</ButtonGroup>

Examples

Nested

"use client"

import { MinusIcon, PlusIcon } from "lucide-react"

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

export function ButtonGroupNested() {
  return (
    <ButtonGroup>
      <ButtonGroup>
        <Button variant="secondary" size="sm">
          1
        </Button>
        <Button variant="secondary" size="sm">
          2
        </Button>
        <Button variant="secondary" size="sm">
          3
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="secondary" size="icon-sm" aria-label="Previous">
          <MinusIcon />
        </Button>
        <Button variant="secondary" size="icon-sm" aria-label="Next">
          <PlusIcon />
        </Button>
      </ButtonGroup>
    </ButtonGroup>
  )
}

Split button with a separator

"use client"

import { ArchiveIcon, ChevronDownIcon, CopyIcon } from "lucide-react"

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

export function ButtonGroupSplit() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <ButtonGroup>
        <Button>
          <CopyIcon data-icon="inline-start" />
          Copy
        </Button>
        <ButtonGroupSeparator />
        <DropdownMenu>
          <DropdownMenuTrigger
            render={<Button size="icon" aria-label="More copy options" />}
          >
            <ChevronDownIcon />
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" className="w-40">
            <DropdownMenuItem>Copy link</DropdownMenuItem>
            <DropdownMenuItem>Copy as Markdown</DropdownMenuItem>
            <DropdownMenuItem>
              <ArchiveIcon />
              Archive
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="secondary">Follow</Button>
        <ButtonGroupSeparator />
        <Button variant="secondary" size="icon" aria-label="More options">
          <ChevronDownIcon />
        </Button>
      </ButtonGroup>
    </div>
  )
}

With input, text and select

.com
"use client"

import { SearchIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

export function ButtonGroupInput() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <ButtonGroup className="w-full max-w-xs">
        <Input placeholder="Search…" aria-label="Search" />
        <Button variant="secondary" size="icon" aria-label="Search">
          <SearchIcon />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <ButtonGroupText render={<Label htmlFor="bg-url" />}>
          https://
        </ButtonGroupText>
        <Input id="bg-url" placeholder="example.com" className="w-40" />
        <ButtonGroupText>.com</ButtonGroupText>
      </ButtonGroup>
      <ButtonGroup>
        <Select items={currencies} defaultValue="usd">
          <SelectTrigger aria-label="Currency">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectGroup>
              {currencies.map((c) => (
                <SelectItem key={c.value} value={c.value}>
                  {c.label}
                </SelectItem>
              ))}
            </SelectGroup>
          </SelectContent>
        </Select>
        <Input placeholder="10.00" aria-label="Amount" className="w-28" />
      </ButtonGroup>
    </div>
  )
}

const currencies = [
  { label: "USD", value: "usd" },
  { label: "EUR", value: "eur" },
  { label: "GBP", value: "gbp" },
]

Vertical

"use client"

import { MinusIcon, PlusIcon } from "lucide-react"

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

export function ButtonGroupVertical() {
  return (
    <ButtonGroup orientation="vertical" aria-label="Zoom">
      <Button variant="secondary" size="icon" aria-label="Zoom in">
        <PlusIcon />
      </Button>
      <Button variant="secondary" size="icon" aria-label="Zoom out">
        <MinusIcon />
      </Button>
    </ButtonGroup>
  )
}

API Reference

ButtonGroup

The ButtonGroup component is a container that groups related buttons together with consistent styling.

PropTypeDefault
orientation"horizontal" | "vertical""horizontal"
<ButtonGroup>
  <Button>Button 1</Button>
  <Button>Button 2</Button>
</ButtonGroup>

Nest multiple button groups to create complex layouts with spacing. See the nested example for more details.

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

ButtonGroupSeparator

The ButtonGroupSeparator component visually divides buttons within a group.

PropTypeDefault
orientation"horizontal" | "vertical""vertical"
<ButtonGroup>
  <Button>Button 1</Button>
  <ButtonGroupSeparator />
  <Button>Button 2</Button>
</ButtonGroup>

ButtonGroupText

Use this component to display text within a button group.

PropTypeDefault
renderReact.ReactElement
<ButtonGroup>
  <ButtonGroupText>Text</ButtonGroupText>
  <Button>Button</Button>
</ButtonGroup>

Use the render prop to render a custom component as the text, for example a label.

import { ButtonGroupText } from "@/components/ui/button-group"
import { Label } from "@/components/ui/label"
 
export function ButtonGroupTextDemo() {
  return (
    <ButtonGroup>
      <ButtonGroupText render={<Label htmlFor="name" />}>Text</ButtonGroupText>
      <Input placeholder="Type something here..." id="name" />
    </ButtonGroup>
  )
}