shadcn.io is not affiliated with official shadcn/ui
Shadcn Button Group for React and Tailwind
Visually connected buttons and related controls.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/button-group.jsonpnpm dlx shadcn@latest add https://kit.dev/r/button-group.jsonnpx shadcn@latest add https://kit.dev/r/button-group.jsonyarn shadcn@latest add https://kit.dev/r/button-group.jsonThis component depends on Separator. Install it first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react tailwind-variantspnpm add @ark-ui/react tailwind-variantsnpm install @ark-ui/react tailwind-variantsyarn add @ark-ui/react tailwind-variantsCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
export const buttonGroupVariants = tv({
base: [
"m-0 min-w-0 border-0 p-0",
"flex w-fit items-stretch",
"*:not([class*='w-']):w-fit",
"*:not([class*='flex-']):flex-1",
"*:focus-visible:relative *:focus-visible:z-10",
"has-[>[data-slot=button-group]]:gap-2",
"has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-e-md",
],
variants: {
orientation: {
horizontal: [
"[&>*:not(:first-child)]:rounded-s-none",
"[&>*:not(:first-child)]:border-s-0",
"[&>*:not(:last-child)]:rounded-e-none",
],
vertical: [
"flex-col",
"[&>*:not(:first-child)]:rounded-t-none",
"[&>*:not(:first-child)]:border-t-0",
"[&>*:not(:last-child)]:rounded-b-none [&>*:not(:last-child)]:shadow-none",
],
},
},
defaultVariants: {
orientation: "horizontal",
},
});
export interface ButtonGroupProps
extends React.ComponentProps<typeof ark.fieldset>,
VariantProps<typeof buttonGroupVariants> {}
export const ButtonGroup = (props: ButtonGroupProps) => {
const { className, orientation = "horizontal", ...rest } = props;
return (
<ark.fieldset
className={cn(buttonGroupVariants({ orientation }), className)}
data-orientation={orientation}
data-slot="button-group"
{...rest}
/>
);
};
export const ButtonGroupText = (
props: React.ComponentProps<typeof ark.div>
) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex items-center gap-2 px-4",
"font-medium text-sm",
"rounded-md border bg-muted shadow-xs",
"[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className
)}
data-slot="button-group-text"
{...rest}
/>
);
};
export const ButtonGroupSeparator = (
props: React.ComponentProps<typeof Separator>
) => {
const { orientation = "vertical", className, ...rest } = props;
return (
<Separator
className={cn(
"relative",
"self-stretch",
"bg-input",
"data-[orientation=vertical]:h-auto",
"m-0!",
className
)}
data-slot="button-group-separator"
orientation={orientation}
{...rest}
/>
);
};Update the import paths to match your project setup.
Usage
import { ArrowLeftIcon, MoreHorizontalIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";<ButtonGroup aria-label="Message actions">
<ButtonGroup className="hidden sm:flex">
<Button aria-label="Go back" size="icon-md" variant="outline">
<ArrowLeftIcon aria-hidden="true" />
</Button>
</ButtonGroup>
<ButtonGroup>
<Button variant="outline">Archive</Button>
<Button variant="outline">Report</Button>
</ButtonGroup>
<ButtonGroup>
<Button variant="outline">Snooze</Button>
<Button aria-label="More options" size="icon-md" variant="outline">
<MoreHorizontalIcon aria-hidden="true" />
</Button>
</ButtonGroup>
</ButtonGroup>Accessibility
| Key | Description |
|---|---|
Enter | Activate the focused control. |
Space | Activate the focused control. |
Demos
Nested
Orientation Horizontal
Orientation Vertical
Separator
With Input
With Menu
With Popover
With Text
API Reference
ButtonGroup
fieldset
PropType
AttributeType
ButtonGroupText
div
PropType
AttributeType
ButtonGroupSeparator
PropType
AttributeType