Button Group

A React button group for toolbars and segmented controls, with shared borders, vertical layout, and split buttons that open a dropdown menu.

Loading preview

Examples

Practical examples and common states for the same installable component.

Default

Related actions share one boundary, separators, and a single overflow menu.

examples/button-group-default.tsx
"use client";
import {
IconChevronLeft,
IconCopy,
IconDots,
IconFileExport,
IconHistory,
IconShare3,
IconTrash,
} from "@tabler/icons-react";
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupMenu,
ButtonGroupMenuContent,
ButtonGroupMenuItem,
ButtonGroupMenuLabel,
ButtonGroupMenuSeparator,
ButtonGroupMenuTrigger,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function DraftActions() {
return (
<ButtonGroup aria-label="Draft actions">
<ButtonGroupItem size="icon" aria-label="Previous draft">
<IconChevronLeft />
</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem>Save draft</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem>Publish</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem>Share</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupMenu>
<ButtonGroupMenuTrigger size="icon" aria-label="More draft actions">
<IconDots />
</ButtonGroupMenuTrigger>
<ButtonGroupMenuContent align="end">
<ButtonGroupMenuLabel>Draft</ButtonGroupMenuLabel>
<ButtonGroupMenuItem icon={<IconCopy />} shortcut="Cmd+D">
Duplicate
</ButtonGroupMenuItem>
<ButtonGroupMenuItem icon={<IconHistory />}>
Version history
</ButtonGroupMenuItem>
<ButtonGroupMenuItem icon={<IconFileExport />}>
Export as PDF
</ButtonGroupMenuItem>
<ButtonGroupMenuSeparator />
<ButtonGroupMenuItem icon={<IconShare3 />}>
Copy invite link
</ButtonGroupMenuItem>
<ButtonGroupMenuSeparator />
<ButtonGroupMenuItem destructive icon={<IconTrash />}>
Delete draft
</ButtonGroupMenuItem>
</ButtonGroupMenuContent>
</ButtonGroupMenu>
</ButtonGroup>
);
}

Split Action

Primary action plus menu trigger, clipped to one rounded control.

examples/button-group-split.tsx
import { IconChevronDown, IconMessageCircle } from "@tabler/icons-react";
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupMenu,
ButtonGroupMenuContent,
ButtonGroupMenuItem,
ButtonGroupMenuTrigger,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function ConversationActions() {
return (
<ButtonGroup aria-label="Conversation actions">
<ButtonGroupItem leftIcon={<IconMessageCircle />}>
Message
</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupMenu>
<ButtonGroupMenuTrigger size="icon" aria-label="Conversation options">
<IconChevronDown />
</ButtonGroupMenuTrigger>
<ButtonGroupMenuContent align="end">
<ButtonGroupMenuItem>Rename thread</ButtonGroupMenuItem>
<ButtonGroupMenuItem>Copy thread link</ButtonGroupMenuItem>
</ButtonGroupMenuContent>
</ButtonGroupMenu>
</ButtonGroup>
);
}

Icon Group

Icon-only actions keep fixed dimensions and accessible labels.

examples/button-group-icons.tsx
import {
IconChevronLeft,
IconChevronRight,
IconLayoutSidebarLeftCollapse,
IconLayoutSidebarRightCollapse,
} from "@tabler/icons-react";
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function PanelActions() {
return (
<ButtonGroup aria-label="Panel layout">
<ButtonGroupItem size="icon" aria-label="Collapse left panel">
<IconLayoutSidebarLeftCollapse />
</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem size="icon" aria-label="Previous item">
<IconChevronLeft />
</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem size="icon" aria-label="Next item">
<IconChevronRight />
</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem size="icon" aria-label="Collapse right panel">
<IconLayoutSidebarRightCollapse />
</ButtonGroupItem>
</ButtonGroup>
);
}

Variants

Group items keep Button variants while the shell owns the shared boundary.

examples/button-group-variants.tsx
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function ButtonGroupVariants() {
return (
<ButtonGroup aria-label="View density">
<ButtonGroupItem>Default</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem variant="secondary">Secondary</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem variant="ghost">Ghost</ButtonGroupItem>
</ButtonGroup>
);
}

Vertical

Stacked groups use horizontal separators and the same item API.

examples/button-group-vertical.tsx
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function VerticalActions() {
return (
<ButtonGroup orientation="vertical" aria-label="Stacked actions">
<ButtonGroupItem>Rename</ButtonGroupItem>
<ButtonGroupSeparator orientation="horizontal" />
<ButtonGroupItem>Duplicate</ButtonGroupItem>
<ButtonGroupSeparator orientation="horizontal" />
<ButtonGroupItem variant="destructive">Delete</ButtonGroupItem>
</ButtonGroup>
);
}

Overview

Button Group is a React and Tailwind CSS button group for toolbars and split buttons, in Next.js or any React app. ButtonGroup wraps its children in a container with the group role, horizontal or vertical, and only the first and last items keep rounded corners so the row reads as one control. Items are ordinary outline buttons by default, so loading states and icon sizes carry over. For a split button, end the row with ButtonGroupMenuTrigger, which opens a Base UI menu with icons, shortcut hints, and destructive items. Give icon-only items an aria-label, since the group cannot name them.

Installation

Run the following command

~/your-project
pnpm dlx wensity@latest add button-group

Usage

Import ButtonGroup from @/components/wensity/button-group and render it anywhere in your React or Next.js app. The source lands in your own repo, so every prop, class and animation below is yours to edit.

app/example.tsx
import {
ButtonGroup,
ButtonGroupItem,
ButtonGroupSeparator,
} from "@/components/wensity/button-group";
export function ButtonGroupDemo() {
return (
<ButtonGroup aria-label="Draft actions">
<ButtonGroupItem>Save draft</ButtonGroupItem>
<ButtonGroupSeparator />
<ButtonGroupItem>Publish</ButtonGroupItem>
</ButtonGroup>
);
}

Props

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Direction the buttons are joined in. Vertical stacks them and moves the shared borders.
  • Gooey Floating Toolbar component preview

    Gooey Floating Toolbar

    Tools share one liquid panel. It pours out of the icon you click, stretches to the next one, then drains back in.

  • Animated Tabs component preview

    Animated Tabs

    A physical stack of photo cards shuffled to the front by clipped tabs, with a tight image inset and copy in the footer.

  • Gooey Dock component preview

    Gooey Dock

    A dock of icons in a solid pill. The active capsule stretches to the item you pick and flips its icon and label inside.

  • macOS Floating Dock component preview

    macOS Floating Dock

    A magnetic dock where icons inflate near the cursor and push their siblings aside.