Button Group
Horizontally joined button sets for related actions — solid, outline, icon-only, and size variants with proper border handling.
Preview
Basic button group
import { ButtonGroup, ButtonGroupItem } from '@/registry/ui-kit/tailwind-button-group/react'
function Example() {
return (
<ButtonGroup>
<ButtonGroupItem>Left</ButtonGroupItem>
<ButtonGroupItem>Center</ButtonGroupItem>
<ButtonGroupItem>Right</ButtonGroupItem>
</ButtonGroup>
)
}Outline variant
import { ButtonGroup, ButtonGroupItem } from '@/registry/ui-kit/tailwind-button-group/react'
function Example() {
return (
<ButtonGroup outline>
<ButtonGroupItem>Day</ButtonGroupItem>
<ButtonGroupItem>Week</ButtonGroupItem>
<ButtonGroupItem>Month</ButtonGroupItem>
</ButtonGroup>
)
}With active state
import { ButtonGroup, ButtonGroupItem } from '@/registry/ui-kit/tailwind-button-group/react'
function Example() {
return (
<ButtonGroup outline>
<ButtonGroupItem>List</ButtonGroupItem>
<ButtonGroupItem active>Grid</ButtonGroupItem>
<ButtonGroupItem>Table</ButtonGroupItem>
</ButtonGroup>
)
}Component API
| Prop | Default | Description |
|---|---|---|
children | — | ButtonGroupItem elements to render inside the group. |
size | "md" | Size applied to all buttons in the group. |
variant | "primary" | Color variant applied to all buttons. |
outline | false | Render outline-style buttons. |
Source Code
"use client";
import { forwardRef, createContext, useContext } from "react";
/* ── Context ── */
type GroupVariant = "primary" | "secondary" | "danger";
type GroupSize = "sm" | "md" | "lg";
interface GroupContext {
variant: GroupVariant;
size: GroupSize;
outline: boolean;
}
const Ctx = createContext<GroupContext>({ variant: "primary", size: "md", outline: false });
/* ── Variant styles ── */
const solidStyles: Record<GroupVariant, string> = {
primary: "bg-indigo-600 text-white hover:bg-indigo-500",
secondary: "bg-zinc-600 text-white hover:bg-zinc-500",
danger: "bg-rose-600 text-white hover:bg-rose-500",
};
const outlineStyles: Record<GroupVariant, string> = {
primary: "border-indigo-300 text-indigo-600 hover:bg-indigo-50 dark:border-indigo-800 dark:text-indigo-400 dark:hover:bg-indigo-950",
secondary: "border-zinc-300 text-zinc-600 hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-900",
danger: "border-rose-300 text-rose-600 hover:bg-rose-50 dark:border-rose-800 dark:text-rose-400 dark:hover:bg-rose-950",
};
const activeStyles: Record<GroupVariant, string> = {
primary: "bg-indigo-600 text-white",
secondary: "bg-zinc-600 text-white",
danger: "bg-rose-600 text-white",
};
const sizeStyles: Record<GroupSize, string> = {
sm: "px-3 py-1.5 text-xs",
md: "px-4 py-2 text-sm",
lg: "px-5 py-2.5 text-base",
};
/* ── ButtonGroup ── */
interface ButtonGroupProps {
variant?: GroupVariant;
size?: GroupSize;
outline?: boolean;
children: React.ReactNode;
className?: string;
}
function ButtonGroup({ variant = "primary", size = "md", outline = false, children, className = "" }: ButtonGroupProps) {
return (
<Ctx.Provider value={{ variant, size, outline }}>
<div
role="group"
className={`inline-flex ${outline ? "border rounded-lg divide-x divide-inherit" : "shadow-sm rounded-lg"} ${outline ? (variant === "primary" ? "border-indigo-300 dark:border-indigo-800" : variant === "danger" ? "border-rose-300 dark:border-rose-800" : "border-zinc-300 dark:border-zinc-700") : ""} ${className}`}
>
{children}
</div>
</Ctx.Provider>
);
}
/* ── ButtonGroupItem ── */
interface ButtonGroupItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
active?: boolean;
children: React.ReactNode;
}
const ButtonGroupItem = forwardRef<HTMLButtonElement, ButtonGroupItemProps>(
({ active = false, className = "", children, ...props }, ref) => {
const { variant, size, outline } = useContext(Ctx);
const base = "inline-flex items-center justify-center font-medium transition-all duration-150 focus-visible:z-10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 disabled:pointer-events-none disabled:opacity-50 first:rounded-l-lg last:rounded-r-lg";
const colorClass = active
? activeStyles[variant]
: outline
? outlineStyles[variant]
: solidStyles[variant];
return (
<button
ref={ref}
className={`${base} ${sizeStyles[size]} ${colorClass} ${className}`}
{...props}
>
{children}
</button>
);
}
);
ButtonGroupItem.displayName = "ButtonGroupItem";
export { ButtonGroup, ButtonGroupItem };
export type { ButtonGroupProps, ButtonGroupItemProps, GroupVariant, GroupSize };
export default ButtonGroup;