npm install @thrivecart/uiyarn add @thrivecart/uipnpm add @thrivecart/uibun add @thrivecart/uiImport the ButtonGroup component from the package:
import { ButtonGroup, Button, IconButton } from '@thrivecart/ui';Buttons visually connected with shared borders
<ButtonGroup attached>
<Button variant="secondary">Left</Button>
<Button variant="secondary">Center</Button>
<Button variant="secondary">Right</Button>
</ButtonGroup>Buttons separated with consistent spacing
<ButtonGroup attached={false}>
<Button variant="primary">Save</Button>
<Button variant="secondary">Cancel</Button>
</ButtonGroup>Common text formatting button group
<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdFormatBold />} aria-label="Bold" />
<IconButton variant="secondary" icon={<MdFormatItalic />} aria-label="Italic" />
<IconButton variant="secondary" icon={<MdFormatUnderlined />} aria-label="Underline" />
</ButtonGroup>Alignment button group
<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdFormatAlignLeft />} aria-label="Align left" />
<IconButton variant="secondary" icon={<MdFormatAlignCenter />} aria-label="Align center" />
<IconButton variant="secondary" icon={<MdFormatAlignRight />} aria-label="Align right" />
</ButtonGroup>Switch between different view modes
<ButtonGroup attached>
<IconButton variant="secondary" icon={<MdViewList />} aria-label="List view" />
<IconButton variant="secondary" icon={<MdViewModule />} aria-label="Grid view" />
<IconButton variant="secondary" icon={<MdViewStream />} aria-label="Stream view" />
</ButtonGroup>Stack buttons vertically
<div className="flex gap-8">
<ButtonGroup orientation="vertical" attached>
<Button variant="secondary">Top</Button>
<Button variant="secondary">Middle</Button>
<Button variant="secondary">Bottom</Button>
</ButtonGroup>
<ButtonGroup orientation="vertical" attached={false}>
<Button variant="primary">Primary Action</Button>
<Button variant="secondary">Secondary Action</Button>
<Button variant="ghost">Tertiary Action</Button>
</ButtonGroup>
</div>Button groups in different sizes
<div className="flex flex-col gap-4 items-start">
<ButtonGroup size="xs">
<Button variant="secondary">XS</Button>
<Button variant="secondary">Size</Button>
<Button variant="secondary">Group</Button>
</ButtonGroup>
<ButtonGroup size="sm">
<Button variant="secondary">Small</Button>
<Button variant="secondary">Size</Button>
<Button variant="secondary">Group</Button>
</ButtonGroup>
<ButtonGroup size="md">
<Button variant="secondary">Default</Button>
<Button variant="secondary">Size</Button>
<Button variant="secondary">Group</Button>
</ButtonGroup>
<ButtonGroup size="lg">
<Button variant="secondary">Large</Button>
<Button variant="secondary">Size</Button>
<Button variant="secondary">Group</Button>
</ButtonGroup>
</div>Single and multi-select toggle groups
Single Select (View)
Multi Select (Formatting)
function ToggleExample() {
const [activeAlign, setActiveAlign] = React.useState('left');
const [formatting, setFormatting] = React.useState({
bold: false,
italic: false,
underline: false,
});
return (
<div className="space-y-6">
{/* Single Select */}
<div>
<h4 className="text-sm font-medium mb-3">Text Alignment (Single Select)</h4>
<ButtonGroup attached>
<IconButton
variant={'secondary'}
data-active={activeAlign === 'left'}
icon={<MdFormatAlignLeft />}
onClick={() => setActiveAlign('left')}
aria-label="Align left"
/>
<IconButton
variant={'secondary'}
data-active={activeAlign === 'center'}
icon={<MdFormatAlignCenter />}
onClick={() => setActiveAlign('center')}
aria-label="Align center"
/>
<IconButton
variant={'secondary'}
data-active={activeAlign === 'right'}
icon={<MdFormatAlignRight />}
onClick={() => setActiveAlign('right')}
aria-label="Align right"
/>
</ButtonGroup>
</div>
{/* Multi Select */}
<div>
<h4 className="text-sm font-medium mb-3">Text Formatting (Multi Select)</h4>
<ButtonGroup attached>
<IconButton
variant={'secondary'}
data-active={formatting.bold}
icon={<MdFormatBold />}
onClick={() => setFormatting(prev => ({ ...prev, bold: !prev.bold }))}
aria-label="Bold"
/>
<IconButton
variant={'secondary'}
data-active={formatting.italic}
icon={<MdFormatItalic />}
onClick={() => setFormatting(prev => ({ ...prev, italic: !prev.italic }))}
aria-label="Italic"
/>
<IconButton
variant={'secondary'}
data-active={formatting.underline}
icon={<MdFormatUnderlined />}
onClick={() => setFormatting(prev => ({ ...prev, underline: !prev.underline }))}
aria-label="Underline"
/>
</ButtonGroup>
</div>
</div>
);
}Use attached mode when buttons represent related options in a single control (e.g., text alignment).
Use detached mode for action pairs like Save/Cancel where actions are distinct but related.
When using as toggle buttons, clearly indicate the active state using the primary variant.
Always provide accessible labels for icon-only buttons in the group.
Use a consistent style within your application for similar use cases.
Only group buttons that are contextually related or form a cohesive control.
| Prop | Type | Default | Description |
|---|---|---|---|
attached | boolean | true | Whether buttons are visually connected |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Size applied to all buttons |
children | ReactNode | - | Button or IconButton components |
className | string | - | Additional className |
ARIA Roles
ButtonGroup uses role="group" to indicate that the buttons are related. For toggle groups, consider using role="toolbar" or implementing a proper ARIA pattern.
role="group"aria-label for icon-only buttonsaria-pressed to indicate state