Installation
npx shadcn@latest add @eo-n/button-group
Install dependencies
npm install @radix-ui/react-slot class-variance-authority
Copy component code
Copy and paste the following code into your project:components/ui/button-group.tsx
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
const buttonGroupVariants = cva(
"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
{
variants: {
orientation: {
horizontal:
"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
vertical:
"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
},
},
defaultVariants: {
orientation: "horizontal",
},
}
);
function ButtonGroup({
className,
orientation,
...props
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
return (
<div
role="group"
data-slot="button-group"
data-orientation={orientation}
className={cn(buttonGroupVariants({ orientation }), className)}
{...props}
/>
);
}
function ButtonGroupText({
className,
asChild = false,
...props
}: React.ComponentProps<"div"> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "div";
return (
<Comp
className={cn(
"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
);
}
function ButtonGroupSeparator({
className,
orientation = "vertical",
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="button-group-separator"
orientation={orientation}
className={cn(
"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto",
className
)}
{...props}
/>
);
}
export {
ButtonGroup,
ButtonGroupSeparator,
ButtonGroupText,
buttonGroupVariants,
};
Update imports
Update the import paths to match your project setup.
Usage
Import all parts and piece them together.
import {
ButtonGroup,
ButtonGroupSeparator,
ButtonGroupText,
} from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
<ButtonGroup>
<Button>First</Button>
<Button>Second</Button>
<Button>Third</Button>
</ButtonGroup>
Examples
Horizontal Group
<ButtonGroup orientation="horizontal">
<Button variant="outline">Bold</Button>
<Button variant="outline">Italic</Button>
<Button variant="outline">Underline</Button>
</ButtonGroup>
Vertical Group
<ButtonGroup orientation="vertical">
<Button variant="outline">Top</Button>
<Button variant="outline">Middle</Button>
<Button variant="outline">Bottom</Button>
</ButtonGroup>
With Separator
<ButtonGroup>
<Button variant="outline">Copy</Button>
<Button variant="outline">Cut</Button>
<ButtonGroupSeparator />
<Button variant="outline">Paste</Button>
</ButtonGroup>
With Text Label
<ButtonGroup>
<ButtonGroupText>View:</ButtonGroupText>
<Button variant="outline">Grid</Button>
<Button variant="outline">List</Button>
</ButtonGroup>
With Icons
<ButtonGroup>
<Button variant="outline" size="icon">
<AlignLeft className="h-4 w-4" />
</Button>
<Button variant="outline" size="icon">
<AlignCenter className="h-4 w-4" />
</Button>
<Button variant="outline" size="icon">
<AlignRight className="h-4 w-4" />
</Button>
</ButtonGroup>
Component API
ButtonGroup
Container for grouping related buttons together.
orientation
'horizontal' | 'vertical'
default:"horizontal"
The orientation of the button group. Determines how buttons are arranged and how borders are removed between adjacent buttons.
Additional CSS classes to apply to the button group container.
ButtonGroupSeparator
Visual separator between button groups or sections within a group.
orientation
'horizontal' | 'vertical'
default:"vertical"
The orientation of the separator. Should match the button group’s orientation for proper alignment.
Additional CSS classes to apply to the separator.
ButtonGroupText
Text label or description within a button group.
When true, merges props with the immediate child element instead of rendering a div wrapper.
Additional CSS classes to apply to the text element.
Features
- Seamless Integration: Automatically handles border radius and border removal between adjacent buttons
- Orientation Support: Works in both horizontal and vertical layouts
- Focus Management: Proper z-index handling ensures focused buttons appear above adjacent buttons
- Flexible Composition: Mix buttons, separators, text labels, and other elements
- Accessible: Uses proper ARIA
role="group" for screen readers
Accessibility
The button group uses role="group" to indicate a set of related controls. Ensure each button within the group has appropriate labels or aria-labels for screen reader users.
Related Components
- Button - Individual button component
- Toggle Group - Grouped toggle buttons with single or multiple selection
- Toolbar - More complex toolbar with various control types