ButtonGroup
Grouped actions behind one joint, and the toolbar shell that gives a strip one tab stop.
@luc/uiExamples
A joined row
The group is geometry and one joint: neighbouring borders collapse, the inner corners square, and every child keeps its own variant.
<ButtonGroup aria-label="Time frame">
<Button variant="secondary">Day</Button>
<Button variant="secondary">Week</Button>
<Button variant="secondary">Month</Button>
</ButtonGroup>Stacked vertically
`vertical` moves the joint to the horizontal edges: the borders collapse upward and the outer corners stay round.
<ButtonGroup vertical aria-label="Chart range">
<Button variant="outline">1 month</Button>
<Button variant="outline">6 months</Button>
<Button variant="outline">1 year</Button>
</ButtonGroup>A change of intent
`ButtonGroupSeparator` says there are two intents here rather than two options; the menu trigger is a real button with its own name, so the row keeps one tab stop per action.
<ButtonGroup>
<Button variant="primary">Save</Button>
<ButtonGroupSeparator />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="primary" aria-label="More save options">
<RiArrowDownSLine />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Save as draft</DropdownMenuItem>
<DropdownMenuItem>Save and publish</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>Toolbar: one tab stop
`Toolbar` is the keyboard contract rather than a wrapper: one tab stop for the strip, arrow keys between the controls, Home and End to the ends, and a `label` the type requires because an unnamed toolbar is a defect. The parts are the Radix primitives re-exported as they are, so a strip brings its own layout and skin; `toggleVariants` is the LUC skin, but it ships in a client module, which is why a server-rendered strip spells the classes out.
<Toolbar label="Chart formatting">
<ToolbarToggleGroup
type="multiple"
defaultValue={["bold"]}
aria-label="Text style"
className="flex items-center gap-1"
>
<ToolbarToggleItem
value="bold"
aria-label="Bold"
className="grid size-8 place-items-center rounded-sm hover:bg-surface-interactive-hover data-[state=on]:bg-surface-selected"
>
<RiBold />
</ToolbarToggleItem>
</ToolbarToggleGroup>
<ToolbarSeparator />
<ToolbarButton
aria-label="Series settings"
className="grid size-8 place-items-center rounded-sm hover:bg-surface-interactive-hover"
>
<RiSettings3Line />
</ToolbarButton>
</Toolbar>Usage
import { ButtonGroup } from "@luc/ui";<ButtonGroup>
<Button variant="secondary">Day</Button>
<Button variant="secondary">Week</Button>
</ButtonGroup>Package setup — peer requirements, the stylesheet and the provider — is on installation.
API
Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.
ButtonGroup, ButtonGroupSeparator and the Toolbar primitives are exports of one module, so both families are documented here: grouping is geometry, and Toolbar is the keyboard contract on top of it.
| Export | Kind | Signature |
|---|---|---|
ButtonGroup | 2 documented props | ({ className, vertical, ...props }: ButtonGroupProps) => import("@types/react").JSX.Element |
ButtonGroupSeparator | export | ({ className, ...props }: React.ComponentPropsWithRef<"span">) => import("@types/react").JSX.Element |
Toolbar | 5 documented props | ({ className, label, ...props }: ToolbarProps & { label: string; }) => import("@types/react").JSX.Element |
ToolbarButton | 1 documented props | import("@types/react").ForwardRefExoticComponent<RadixToolbar.ToolbarButtonProps & import("@types/react").RefAttributes<HTMLButtonElement>> |
ToolbarLink | 1 documented props | import("@types/react").ForwardRefExoticComponent<RadixToolbar.ToolbarLinkProps & import("@types/react").RefAttributes<HTMLAnchorElement>> |
ToolbarSeparator | 3 documented props | import("@types/react").ForwardRefExoticComponent<RadixToolbar.ToolbarSeparatorProps & import("@types/react").RefAttributes<HTMLDivElement>> |
ToolbarToggleGroup | 10 documented props | import("@types/react").ForwardRefExoticComponent<(RadixToolbar.ToolbarToggleGroupSingleProps | RadixToolbar.ToolbarToggleGroupMultipleProps) & import("@types/react").RefAttributes<HTMLDivElement>> |
ToolbarToggleItem | 2 documented props | import("@types/react").ForwardRefExoticComponent<RadixToolbar.ToolbarToggleItemProps & import("@types/react").RefAttributes<HTMLButtonElement>> |
ButtonGroup props
Type: ButtonGroupProps
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
aria-describedby? | string | undefined | — | Attach the group to a `Field`'s accessible description. |
vertical? | boolean | undefined | — | Stack vertically. Default `false`. |
Toolbar props
Type: RadixToolbar.ToolbarProps & RefAttributes<HTMLDivElement> & { label: string; }
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
dir?inherited | Direction | undefined | — | |
label | string | — | |
loop?inherited | boolean | undefined | — | |
orientation?inherited | "horizontal" | "vertical" | undefined | — |
ToolbarButton props
Type: ToolbarButtonProps & RefAttributes<HTMLButtonElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — |
ToolbarLink props
Type: ToolbarLinkProps & RefAttributes<HTMLAnchorElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — |
ToolbarSeparator props
Type: ToolbarSeparatorProps & RefAttributes<HTMLDivElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
decorative?inherited | boolean | undefined | — | Whether or not the component is purely decorative. When true, accessibility-related attributes are updated so that that the rendered element is removed from the accessibility tree. |
orientation?inherited | "horizontal" | "vertical" | undefined | — | Either `vertical` or `horizontal`. Defaults to `horizontal`. |
ToolbarToggleGroup props
Type: (ToolbarToggleGroupSingleProps | ToolbarToggleGroupMultipleProps) & RefAttributes<HTMLDivElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
defaultValue?inherited | string | string[] | undefined | — | The value of the item that is pressed when initially rendered. Use `defaultValue` if you do not need to control the state of a toggle group. The value of the items that are presse… |
dir?inherited | Direction | undefined | — | |
disabled?inherited | boolean | undefined | — | Whether the group is disabled from user interaction. |
loop?inherited | boolean | undefined | — | |
onValueChange?inherited | ((value: string) => void) | ((value: string[]) => void) | undefined | — | The callback that fires when the value of the toggle group changes. The callback that fires when the state of the toggle group changes. |
orientation?inherited | "horizontal" | "vertical" | undefined | — | |
rovingFocus?inherited | boolean | undefined | — | Whether the group should maintain roving focus of its buttons. |
typeinherited | "single" | "multiple" | — | |
value?inherited | string | string[] | undefined | — | The controlled stateful value of the item that is pressed. The controlled stateful value of the items that are pressed. |
ToolbarToggleItem props
Type: ToolbarToggleItemProps & RefAttributes<HTMLButtonElement>
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
asChild?inherited | boolean | undefined | — | |
valueinherited | string | — | A string value for the toggle group item. All items within a toggle group should use a unique value. |
Guidelines
Do
- Use `ButtonGroup` for two to four actions that belong to one control, and leave every child with its own variant so a danger action still reads as dangerous.
- Use `vertical` for a stack: the joint moves to the horizontal edges and the inner corners square the same way.
- Use `ButtonGroupSeparator` to mark a change of intent inside a mixed group — a primary action, then its menu.
- Use `Toolbar` when the strip needs one tab stop and arrow-key movement rather than a tab stop per control.
Don't
- Group actions that are not related: the joint says the parts are one control with several options.
- Restate the group's geometry with your own negative margins or `rounded-*` classes — that is what the joint owns.
- Hand-roll `role="toolbar"` or a roving-focus loop instead of the `Toolbar` primitive, which already provides both.
Accessibility
`ButtonGroup` is a `role="group"` container that adds no keyboard behaviour: every child keeps its own tab stop, which is right for a row of unrelated actions and wrong for a formatting strip. `Toolbar` is the accessible alternative — one tab stop for the whole strip, arrow keys between the controls, and a required `label` that names the toolbar.
Family
Actions
Things a person presses, and the chrome that gathers them.
8 documented entries in this family. Open the family index
Related