Antares
Components

ToggleButton

Accessible toggle button component for grouped contextual actions with single or multiple selection, keyboard navigation, and RTL-aware layouts.

Installation

npm install --save @godaddy/antares

Props

NameTypeDefault
children?ReactNode

The content of the item. Supports text, icon, or icon + text. For icon-only items, provide an `aria-label` for screen readers.

className?string | undefined

Additional class names applied to the item element.

id?Key | undefined

When used in a ToggleButtonGroup, an identifier for the item in `selectedKeys`. When used standalone, a DOM id.

preventFocusOnPress?boolean | undefined

Whether to prevent focus from moving to the button when pressing it. Caution, this can make the button inaccessible and should only be used when alternative keyboard interaction is provided, such as ComboBox's MenuTrigger or a NumberField's increment/decrement control.

excludeFromTabOrder?boolean | undefined

Whether to exclude the element from the sequential tab order. If true, the element will not be focusable via the keyboard by tabbing. This should be avoided except in rare scenarios where an alternative means of accessing the element or its functionality via the keyboard is available.

isSelected?boolean | undefined

Whether the element should be selected (controlled).

defaultSelected?boolean | undefined

Whether the element should be selected (uncontrolled).

onChange?((isSelected: boolean) => void) | undefined

Handler that is called when the element's selection state changes.

isDisabled?boolean | undefined

Whether the button is disabled.

onPress?((e: PressEvent) => void) | undefined

Handler that is called when the press is released over the target.

onPressStart?((e: PressEvent) => void) | undefined

Handler that is called when a press interaction starts.

onPressEnd?((e: PressEvent) => void) | undefined

Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.

onPressChange?((isPressed: boolean) => void) | undefined

Handler that is called when the press state changes.

onPressUp?((e: PressEvent) => void) | undefined

Handler that is called when a press is released over the target, regardless of whether it started on the target or not.

onClick?((e: React.MouseEvent<FocusableElement>) => void) | undefined

**Not recommended – use `onPress` instead.** `onClick` is an alias for `onPress` provided for compatibility with other libraries. `onPress` provides additional event details for non-mouse interactions.

autoFocus?boolean | undefined

Whether the element should receive focus on render.

onFocus?((e: React.FocusEvent<Element, Element>) => void) | undefined

Handler that is called when the element receives focus.

onBlur?((e: React.FocusEvent<Element, Element>) => void) | undefined

Handler that is called when the element loses focus.

onFocusChange?((isFocused: boolean) => void) | undefined

Handler that is called when the element's focus status changes.

onKeyDown?((e: KeyboardEvent) => void) | undefined

Handler that is called when a key is pressed.

onKeyUp?((e: KeyboardEvent) => void) | undefined

Handler that is called when a key is released.

onHoverStart?((e: HoverEvent) => void) | undefined

Handler that is called when a hover interaction starts.

onHoverEnd?((e: HoverEvent) => void) | undefined

Handler that is called when a hover interaction ends.

onHoverChange?((isHovering: boolean) => void) | undefined

Handler that is called when the hover state changes.

slot?string | null | undefined

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent.

style?StyleOrFunction<ToggleButtonRenderProps> | undefined

The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.

render?DOMRenderFunction<"button", ToggleButtonRenderProps> | undefined

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

Examples

Default (Single Selection)

A ToggleButton toggles between selected and unselected. Wrap multiple inside a ToggleButtonGroup to link them with shared selection and keyboard navigation — defaultSelectedKeys sets the initial selection.

import { ToggleButtonGroup, ToggleButton } from '@godaddy/antares';export function DefaultExample() {  return (    <ToggleButtonGroup aria-label="View" defaultSelectedKeys={['day']} selectionMode="single">      <ToggleButton id="day">Day</ToggleButton>      <ToggleButton id="week">Week</ToggleButton>      <ToggleButton id="month">Month</ToggleButton>    </ToggleButtonGroup>  );}

Multiple Selection

Use selectionMode="multiple" to allow more than one item to be active at a time.

import { ToggleButtonGroup, ToggleButton } from '@godaddy/antares';export function MultipleSelectionExample() {  return (    <ToggleButtonGroup aria-label="Text formatting" selectionMode="multiple" defaultSelectedKeys={['bold']}>      <ToggleButton id="bold">Bold</ToggleButton>      <ToggleButton id="italic">Italic</ToggleButton>      <ToggleButton id="underline">Underline</ToggleButton>    </ToggleButtonGroup>  );}

Controlled

Use selectedKeys and onSelectionChange to control selection from parent state — onSelectionChange receives a Set<string> of the selected item id values.

Current selection: week

import { ToggleButtonGroup, ToggleButton } from '@godaddy/antares';import { useState } from 'react';export function ControlledExample() {  const [selected, setSelected] = useState<Set<string>>(new Set(['week']));  return (    <>      <ToggleButtonGroup        aria-label="View"        selectionMode="single"        selectedKeys={selected}        onSelectionChange={(keys) => setSelected(keys as Set<string>)}      >        <ToggleButton id="day">Day</ToggleButton>        <ToggleButton id="week">Week</ToggleButton>        <ToggleButton id="month">Month</ToggleButton>      </ToggleButtonGroup>      <p>Current selection: {Array.from(selected).join(', ')}</p>    </>  );}

Disabled States

Disable the entire group with isDisabled on the container, or disable individual items with isDisabled on specific ToggleButton elements.

import { ToggleButtonGroup, ToggleButton, Flex } from '@godaddy/antares';export function DisabledExample() {  return (    <Flex direction="column" gap="sm" alignItems="start">      <label>Disabled group</label>      <ToggleButtonGroup aria-label="Disabled group" isDisabled defaultSelectedKeys={['day']} selectionMode="single">        <ToggleButton id="day">Day</ToggleButton>        <ToggleButton id="week">Week</ToggleButton>        <ToggleButton id="month">Month</ToggleButton>      </ToggleButtonGroup>      <label>Individual disabled item</label>      <ToggleButtonGroup aria-label="Individual disabled" defaultSelectedKeys={['day']} selectionMode="single">        <ToggleButton id="day">Day</ToggleButton>        <ToggleButton id="week" isDisabled>          Week        </ToggleButton>        <ToggleButton id="month">Month</ToggleButton>      </ToggleButtonGroup>    </Flex>  );}

Sizes

Two sizes are available: sm and md (default).

import { ToggleButtonGroup, ToggleButton, Flex } from '@godaddy/antares';export function SizesExample() {  return (    <Flex direction="column" gap="md" alignItems="start">      <ToggleButtonGroup aria-label="Size sm" size="sm" defaultSelectedKeys={['day']} selectionMode="single">        <ToggleButton id="day">Day</ToggleButton>        <ToggleButton id="week">Week</ToggleButton>        <ToggleButton id="month">Month</ToggleButton>      </ToggleButtonGroup>      <ToggleButtonGroup aria-label="Size md" size="md" defaultSelectedKeys={['day']} selectionMode="single">        <ToggleButton id="day">Day</ToggleButton>        <ToggleButton id="week">Week</ToggleButton>        <ToggleButton id="month">Month</ToggleButton>      </ToggleButtonGroup>    </Flex>  );}

Icon + Text

Items can include an Icon alongside text for added visual context.

import { ToggleButtonGroup, ToggleButton, Icon } from '@godaddy/antares';export function IconAndTextExample() {  return (    <ToggleButtonGroup aria-label="Layout" defaultSelectedKeys={['list']} selectionMode="single">      <ToggleButton id="list">        <Icon icon="bulleted-list" />        List      </ToggleButton>      <ToggleButton id="grid">        <Icon icon="grid" />        Grid      </ToggleButton>    </ToggleButtonGroup>  );}

Icon Only

For compact layouts, items can contain only icons. Padding adjusts automatically to keep the button square — no extra prop required. Always provide an aria-label on each icon-only item so screen readers can identify the action.

import { ToggleButtonGroup, ToggleButton, Icon } from '@godaddy/antares';export function IconOnlyExample() {  return (    <ToggleButtonGroup aria-label="Layout" defaultSelectedKeys={['list']} selectionMode="single">      <ToggleButton id="list" aria-label="List view">        <Icon icon="bulleted-list" />      </ToggleButton>      <ToggleButton id="grid" aria-label="Grid view">        <Icon icon="grid" />      </ToggleButton>    </ToggleButtonGroup>  );}

With Dropdown

Wrap a ToggleButton in a MenuTrigger to add sub-options to a group item.

import { useState } from 'react';import { ToggleButtonGroup, ToggleButton, Icon, Menu, MenuItem, MenuTrigger } from '@godaddy/antares';type Alignment = 'left' | 'center' | 'right';const ALIGNMENT_ICONS: Record<Alignment, string> = {  left: 'align-left',  center: 'align-center',  right: 'align-right'};const ALIGNMENT_LABELS: Record<Alignment, string> = {  left: 'Align Left',  center: 'Align Center',  right: 'Align Right'};export function WithDropdownExample() {  const [formatting, setFormatting] = useState<Set<string>>(new Set());  const [[alignment], setAlignment] = useState<Set<Alignment>>(new Set(['left']));  return (    <ToggleButtonGroup      aria-label="Text formatting"      selectionMode="multiple"      selectedKeys={formatting}      onSelectionChange={(keys) => setFormatting(new Set([...(keys as Set<string>)].filter((k) => k !== 'align')))}    >      <ToggleButton id="bold" aria-label="Bold">        <Icon icon="bold" />      </ToggleButton>      <ToggleButton id="italic" aria-label="Italic">        <Icon icon="italic" />      </ToggleButton>      <MenuTrigger>        <ToggleButton id="align" aria-label={ALIGNMENT_LABELS[alignment]}>          <Icon icon={ALIGNMENT_ICONS[alignment]} />          <Icon icon="chevron-down" />        </ToggleButton>        <Menu          selectionMode="single"          selectedKeys={[alignment]}          onSelectionChange={(keys) => setAlignment(new Set([...(keys as Set<Alignment>)]))}        >          <MenuItem id="left">{ALIGNMENT_LABELS.left}</MenuItem>          <MenuItem id="center">{ALIGNMENT_LABELS.center}</MenuItem>          <MenuItem id="right">{ALIGNMENT_LABELS.right}</MenuItem>        </Menu>      </MenuTrigger>    </ToggleButtonGroup>  );}

RTL Direction

The group follows the document's layout direction, detected automatically from the browser locale.

import { RTLProvider } from '../../../utils/rtl-locale-provider.tsx';import { DefaultExample } from './default.tsx';export function RTLExample() {  return (    <RTLProvider>      <DefaultExample />    </RTLProvider>  );}

Customization

Data Attributes

Components automatically add data attributes for styling different states:

ToggleButtonGroup Container: data-size

ToggleButton Item: data-selected, data-hovered, data-pressed, data-focus-visible, data-disabled

.my-toggle-group [data-selected] {
  background-color: #09757a;
  color: #fff;
}

.my-toggle-group [data-focus-visible] {
  outline: 2px solid #1976d2;
  outline-offset: 2px;
}

.my-toggle-group [data-disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

Component Customization

Individual components can be styled by passing className props:

<ToggleButtonGroup aria-label="View" className="custom-toggle-group">
  <ToggleButton id="list" className="custom-item">List</ToggleButton>
  <ToggleButton id="grid" className="custom-item">Grid</ToggleButton>
</ToggleButtonGroup>

Accessibility

Keyboard Navigation

KeyAction
TabMoves focus to/from the button group (single tab stop)
Arrow Left / RightMove focus between items (RAC automatically reverses in RTL)
Space / EnterToggle the focused item
HomeMove focus to the first item
EndMove focus to the last item

ARIA Support

  • selectionMode="single"role="radiogroup" on container, role="radio" + aria-checked per item
  • selectionMode="multiple"role="toolbar" on container, role="button" + aria-pressed per item
  • aria-label (or aria-labelledby) is required on the group container
  • Icon-only items require aria-label on each ToggleButton

Troubleshooting

Selection Not Updating

// ❌ Wrong: Using both selectedKeys and defaultSelectedKeys
<ToggleButtonGroup selectedKeys={keys} defaultSelectedKeys={['day']}>
  <ToggleButton id="day">Day</ToggleButton>
</ToggleButtonGroup>

// ✅ Controlled mode
<ToggleButtonGroup selectedKeys={keys} onSelectionChange={setKeys}>
  <ToggleButton id="day">Day</ToggleButton>
</ToggleButtonGroup>

// ✅ Uncontrolled mode
<ToggleButtonGroup defaultSelectedKeys={['day']}>
  <ToggleButton id="day">Day</ToggleButton>
</ToggleButtonGroup>

Styling Overrides Not Applying

/* ❌ May not have enough specificity */
.my-custom-item {
  background-color: red;
}

/* ✅ Use data attributes with className for higher specificity */
.my-toggle-group [data-selected] {
  background-color: red;
}

Keyboard Navigation Not Working

/* ❌ Don't remove focus outlines without replacement */
[data-focus-visible] {
  outline: none;
}

/* ✅ Provide visible focus indicator */
.my-toggle-group [data-focus-visible] {
  outline: 2px solid #1976d2;
  outline-offset: 2px;
}

Best Practices

When to Use

  • ✅ For grouped contextual actions — two or more closely related actions that belong together (e.g. Bold / Italic / Underline, Align Left / Center / Right)
  • ✅ For compact action grouping — related actions in space-constrained layouts (e.g. Undo / Redo, Zoom In / Out / Reset)
  • ✅ For multi-select scenarios — when users need to select multiple options simultaneously
  • ❌ For unrelated actions that perform disconnected tasks without shared context
  • ❌ For more than 5 choices — cognitive overload; consider a Select instead
  • ❌ When actions require strong visual distinction (e.g. Save vs. Delete)
  • ❌ When selecting an option leads to a different page or workflow — use navigation instead
  • ❌ For mutually exclusive content views (e.g. Monthly / Yearly) — use SegmentedController instead

Keep Variants Consistent

Avoid mixing different content types (text-only, icon-only, icon+text) within the same group. Mixing styles creates visual noise and makes the set feel fragmented rather than unified.

Balance Label Lengths

Keep button labels of similar length. Large differences in text length cause irregular button widths and make the group appear uneven. Prefer short, concise wording.

Alignment

Align the group to reflect its relationship to surrounding content:

  • Left — when the group controls content directly below or within the same column (dashboards, tables).
  • Center — when the group acts as a prominent toggle affecting an entire section or page.
  • Right — when the group represents utility or contextual controls (e.g. grid/list view toggle).

Label Guidelines

  • Use title case: capitalize the first letter of each word, except short prepositions.
  • Keep labels to one or two words. Omit articles (a, an, the) and filler words.
  • Prefer action-focused or descriptive language that communicates the option instantly.

Icon-Only Items

Use icon-only items only when the icon's meaning is universally recognized. Always provide an aria-label on each icon-only ToggleButton:

<ToggleButton id="list" aria-label="List view">
  <Icon icon="bulleted-list" />
</ToggleButton>

On this page