Button Layout
The ButtonLayout component allows you to group multiple buttons together, either horizontally or vertically. It is useful for organizing related actions.
Installation
import { Button, ButtonLayout } from '@harnessio/ui/components'
return ( <ButtonLayout horizontalAlign="start"> <Button>Button primary</Button> <Button variant="secondary">Button secondary</Button> <Button variant="outline">Button outline</Button> </ButtonLayout>
<ButtonLayout orientation="vertical"> <Button>Button primary</Button> <Button variant="secondary">Button secondary</Button> <Button variant="outline">Button outline</Button> </ButtonLayout>
<ButtonLayout.Root> <ButtonLayout.Primary> <Button>Button primary</Button> </ButtonLayout.Primary> <ButtonLayout.Secondary> <Button variant="secondary">Button secondary</Button> </ButtonLayout.Secondary> </ButtonLayout.Root>)Layout Orientation
The ButtonLayout component accepts an orientation prop, which defines the layout direction: either horizontal or vertical.
Horizontal Alignment
When orientation is horizontal, the horizontalAlign prop controls alignment. Use start to align buttons to the left, or end to align them to the right.
Primary and secondary button groups
The component provides ButtonLayout.Primary and ButtonLayout.Secondary compositions for grouping buttons.
When orientation is set to horizontal:
Primaryaligns buttons to the rightSecondaryaligns buttons to the left
When orientation is set to horizontal and horizontalAlign is set to start:
Primaryaligns buttons to the leftSecondaryaligns buttons to the right
When orientation is set to vertical:
Primaryplaces buttons at the topSecondaryplaces buttons at the bottom
Anatomy
ButtonLayout is a layout wrapper — it positions and spaces a set of buttons, but has no visuals of its own. All parts can be imported and composed as needed:
<ButtonLayout.Root> <ButtonLayout.Primary /> <ButtonLayout.Secondary /></ButtonLayout.Root>- Root — the container. Sets the
orientation(horizontalby default, orvertical) and, when horizontal, thehorizontalAlign(endby default, orstart). Buttons passed directly toRootare laid out in order. - Primary (optional) — a slot for the main action. When horizontal it sits on the far side of the alignment (right by default); when vertical it sits on top.
- Secondary (optional) — a slot for supporting actions. It sits opposite
Primary— left when horizontal, bottom when vertical.
Using the Primary / Secondary slots is what pins actions to opposite ends of the row; passing buttons straight to Root simply flows them together.
Best practices
Do
- Use
ButtonLayoutanywhere you have a set of related actions to place — dialog footers, form actions, toolbars. - Keep one clear primary action. Put it in
ButtonLayout.Primary(or last, when passing buttons directly) so it lands in the emphasized position. - Follow the default right-aligned, primary-last order for confirming flows like dialogs and forms; it matches where people expect the confirm action.
- Switch to
orientation="vertical"in narrow columns or stacked mobile layouts instead of letting a horizontal row wrap.
Don’t
- Don’t stack multiple primary buttons in one layout; competing emphasis makes the main action harder to find.
- Don’t rely on
ButtonLayoutfor grouping semantics — it only positions buttons. If the buttons form a connected set of choices, reach for Button Group instead. - Don’t hand-space buttons with margins to fake this layout; use the
orientationandhorizontalAlignprops so spacing stays consistent.
Content
ButtonLayouthas no text of its own — label writing lives with the Button inside it.- Use sentence case for every button label — capitalize only the first word, along with any proper nouns or product names.
- Order buttons by importance and keep the primary action’s label specific to the outcome (“Save changes”, “Delete”) rather than a generic “OK”.
Accessibility
ButtonLayout renders plain <div> elements with no ARIA roles — it is purely presentational. The accessibility of the group therefore comes entirely from the Button components placed inside it.
Keyboard
- Each button inside the layout is a native
<button>and an individual stop in the tab order, activated with Enter and Space. ButtonLayoutadds no keyboard behavior of its own — there is no arrow-key movement between the buttons.
Labeling
- Buttons are named by their own text or, for icon-only buttons, their tooltip/
aria-label.ButtonLayoutneither adds nor overrides any accessible name.
Focus & states
- Focus order follows the DOM order of the buttons, not their visual position (see the limitation below). Each button shows the standard focus ring (
:focus-visible) via the--cn-focustoken.
Known limitations
- Visual order can diverge from focus order.
ButtonLayout.Primaryis moved to the far end visually with CSS (order+margin), so aPrimaryslot authored first in the markup still renders on the right but is focused first. For predictable keyboard flow, author the buttons in the reading order you want people to tab through, and let the slots handle visual placement. - No group semantics. All parts are plain
<div>s with norole="group"or group label, so assistive tech announces the buttons individually rather than as a named set. If the grouping carries meaning, add your ownrole="group"andaria-labelaround it.
Properties
Root
The Root component serves as the main container for Button, ButtonLayout.Primary and ButtonLayout.Secondary components.
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| orientation | false | horizontal | 'horizontal' | 'vertical' |
| horizontalAlign | false | end | 'start' | 'end' |
| className | false | string |
Primary
ButtonLayout.Primary is placed on top when orientation is vertical
ButtonLayout.Primary appears on the right when orientation is horizontal
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| className | false | string |
Secondary
ButtonLayout.Secondary is placed on bottom when orientation is vertical
ButtonLayout.Secondary appears on the left when orientation is horizontal
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| className | false | string |