Skip to content

Pro pre-sale is open. Installation opens January 2027. See Pro

Components

ButtonGroup

A named row or column of related Buttons, joined into one block with shared edges or spaced apart. The group sets one size and variant for its Buttons.

  • npm: 1 direct, 4 total
  • registry: 3 direct, 3 total

Preview

Props

  • orientation
  • attached
  • size
  • variant
  • fullWidth
  • content
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"

<ButtonGroup aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>

Usage

When to use

  • Keep two to five related actions together, such as Copy, Edit and Delete in a toolbar.
  • A main action with a menu of more next to it (a split button).

When not to use

  • A choice that stays selected, such as a view or alignment. Use ToggleGroup or Tabs.
  • Unrelated actions, or a main action next to Cancel. Place the Buttons side by side.
  • Moving between pages of results. Use Pagination.

Variants

orientation

A row or a column of Buttons.

  • horizontaldefault

  • vertical

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

<ButtonGroup aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup orientation="vertical" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>

size

The Button size for every Button in the group, the same steps as Button: sm 28px, md 32px, lg 36px (the group default), xl 44px with a mouse; on touch md is 36px, lg 44px and xl 48px. A Button that sets its own size keeps it.

  • sm

  • md

  • lgdefault

  • xl

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

<ButtonGroup size="sm" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup size="md" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup size="xl" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>

variant

The Button variant for every Button in the group. A Button that sets its own variant keeps it. An attached group draws one shadow for the variant (shadow-low for secondary and solid, shadow-med for the glossy ones) instead of one per Button. Put a ButtonGroupSeparator between attached solid Buttons.

  • secondarydefault

  • solid

  • soft

  • ghost

  • destructive

  • destructive-soft

  • glossy

  • destructive-glossy

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

<ButtonGroup aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="solid" aria-label="File actions">
  <Button>Duplicate</Button>
  <ButtonGroupSeparator />
  <Button>Edit</Button>
  <ButtonGroupSeparator />
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="soft" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="ghost" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="destructive" aria-label="File actions">
  <Button>Duplicate</Button>
  <ButtonGroupSeparator />
  <Button>Edit</Button>
  <ButtonGroupSeparator />
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="destructive-soft" aria-label="File actions">
  <Button>Duplicate</Button>
  <Button>Edit</Button>
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="glossy" aria-label="File actions">
  <Button>Duplicate</Button>
  <ButtonGroupSeparator />
  <Button>Edit</Button>
  <ButtonGroupSeparator />
  <Button>Delete</Button>
</ButtonGroup>
<ButtonGroup variant="destructive-glossy" aria-label="File actions">
  <Button>Duplicate</Button>
  <ButtonGroupSeparator />
  <Button>Edit</Button>
  <ButtonGroupSeparator />
  <Button>Delete</Button>
</ButtonGroup>

Examples

Attached actions

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

<ButtonGroup aria-label="File actions">
  <Button>Rename</Button>
  <Button>Duplicate</Button>
  <Button>Archive</Button>
</ButtonGroup>

Icons and labels

Icon-only Buttons carry their own name.

import { IconBold, IconItalic, IconUnderline } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"

<ButtonGroup aria-label="Text formatting">
  <Button iconOnly aria-label="Bold">
    <IconBold size={16} />
  </Button>
  <Button iconOnly aria-label="Italic">
    <IconItalic size={16} />
  </Button>
  <Button iconOnly aria-label="Underline">
    <IconUnderline size={16} />
  </Button>
</ButtonGroup>

Split button

A main action and a menu with more. Solid Buttons have no border, so a separator marks where one ends.

import { IconChevronDown, IconSparkles } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup, ButtonGroupSeparator } from "@/components/ui/button-group"

<ButtonGroup aria-label="Publish" variant="solid">
  <Button leadingIcon={<IconSparkles size={16} />}>Publish</Button>
  <ButtonGroupSeparator />
  <Button iconOnly aria-label="More publish options">
    <IconChevronDown size={16} />
  </Button>
</ButtonGroup>

Spaced apart

import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"

<ButtonGroup aria-label="Pages" attached={false}>
  <Button leadingIcon={<IconArrowLeft size={16} />}>Previous</Button>
  <Button trailingIcon={<IconArrowRight size={16} />}>Next</Button>
</ButtonGroup>

Vertical

import { IconMinus, IconPlus } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"

<ButtonGroup aria-label="Zoom" orientation="vertical">
  <Button iconOnly aria-label="Zoom in">
    <IconPlus size={16} />
  </Button>
  <Button iconOnly aria-label="Zoom out">
    <IconMinus size={16} />
  </Button>
</ButtonGroup>

Full width

import { IconDownload, IconLink, IconMail } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"

<ButtonGroup aria-label="Share" fullWidth>
  <Button leadingIcon={<IconLink size={16} />}>Share link</Button>
  <Button leadingIcon={<IconMail size={16} />}>Email</Button>
  <Button leadingIcon={<IconDownload size={16} />}>Download</Button>
</ButtonGroup>

Do and don't

Do

  • Group two to five closely related actions, such as Rename, Duplicate and Archive, or a main action and a menu with more.
  • Name the group with aria-label or a visible heading through aria-labelledby.
  • Use one variant for the whole group. Use a ButtonGroupSeparator between attached solid Buttons.

Don't

  • Do not group unrelated actions or a main action with a Cancel. Place those Buttons side by side instead.
  • Do not use a group for a choice that stays selected; use ToggleGroup or Tabs.
  • Do not put a link Button in a group.

Props

NameTypeValuesDefaultDescription
orientationenumhorizontal | verticalhorizontalA row or a column of Buttons.
attachedboolean-trueJoins the Buttons into one block with shared edges. false spaces them out with a spacing-sm gap.
sizeenumsm | md | lg | xllgThe Button size for every Button in the group, the same steps as Button: sm 28px, md 32px, lg 36px (the group default), xl 44px with a mouse; on touch md is 36px, lg 44px and xl 48px. A Button that sets its own size keeps it.
variantenumsecondary | solid | soft | ghost | destructive | destructive-soft | glossy | destructive-glossysecondaryThe Button variant for every Button in the group. A Button that sets its own variant keeps it. An attached group draws one shadow for the variant (shadow-low for secondary and solid, shadow-med for the glossy ones) instead of one per Button. Put a ButtonGroupSeparator between attached solid Buttons.
fullWidthboolean-falseFills the width of its container. In a row the Buttons share the width equally.

Slots

NameRequiredDescription
childrenyesButtons, and ButtonGroupSeparator between attached solid Buttons. Only direct Button children take the group size and variant.

Anatomy

PartDescription
rootThe named group. It draws no fill: the Buttons keep their own fill, border and motion. Attached, the Buttons drop their own shadows and the group draws one shadow (the variant's step) around the whole block.
itemA Button inside the group. Attached, only the outer corners stay round and neighbouring borders collapse into one line.
separatorAn optional 1px line between two solid Buttons, which have no border to show where one ends.

Accessibility

Role: group

KeysAction
Tab, Shift+TabMoves through the Buttons one by one. Every Button keeps its own tab stop.
Space, EnterPresses the focused Button.
  • The group requires aria-label or aria-labelledby, so screen readers announce what the Buttons belong to.
  • Every Button keeps its own name. An icon-only Button requires aria-label.
  • The focused Button rises above its neighbours, so its focus ring is never cut by the next Button.
  • ButtonGroupSeparator is decorative and hidden from screen readers.
  • For a choice that stays selected, use ToggleGroup or Tabs: the Buttons of a group carry no pressed state.

Install

npx werewolf-ui add button-group

Run npx werewolf-ui init once in your app before adding components. Init sets up tokens, their CSS import and agent rules. Add installs this component and its dependencies while preserving your theme. See Installation for requirements and the alternative registry setup.