BetaActions

ToggleButton

A button that keeps whether it is active. It is for independent choices such as Like, Favourite, Pin or Mute — one press turns the choice on, the next turns it off.

Overview

A button that keeps whether it is active. It is for independent choices such as Like, Favourite, Pin or Mute — one press turns the choice on, the next turns it off.

import { ToggleButton } from '@xaui/native/toggle-button'
;<ToggleButton defaultSelected>Like</ToggleButton>

It follows the Button scale and composition model, but selection is part of its value, not a momentary pressed visual.

First example

<ToggleButton defaultSelected onSelectedChange={saveFavourite}>
Favourite
</ToggleButton>

Anatomy

<ToggleButton>
  <ToggleButton.Icon />
  <ToggleButton.Label />
</ToggleButton>
  • ToggleButton — the pressable root. It owns or receives selection, resolves the recipe once and publishes the selected styles and values.
  • ToggleButton.Icon — an icon that inherits the root's size and current colour.
  • ToggleButton.Label — single-line text that inherits the current colour and type.

The view depth is one: PressableFeedback > (Icon | Text). JSX order is screen order, and neither slot carries a margin.

Usage

Uncontrolled

defaultSelected supplies the initial value; the root owns it afterwards.

<ToggleButton defaultSelected onSelectedChange={saveFavourite}>
  Favourite
</ToggleButton>

Controlled

const [liked, setLiked] = useState(false)

<ToggleButton isSelected={liked} onSelectedChange={setLiked}>
  Like
</ToggleButton>

onSelectedChange fires with the proposed next value in both modes. A controlled root does not store that value; the caller decides whether it moves.

Changing the mark

A render child receives the current state. This is how the reference's outline heart turns into a filled heart without a prop configuring the inside of the component.

<ToggleButton>
  {({ isSelected }) => (
    <>
      <ToggleButton.Icon as={isSelected ? HeartFilled : HeartOutline} />
      <ToggleButton.Label>Like</ToggleButton.Label>
    </>
  )}
</ToggleButton>

Text shorthand

Stringifiable children are wrapped in ToggleButton.Label automatically.

<ToggleButton>Pin</ToggleButton>

Exclusive group

ToggleButton.Group gives a set one selected value. A member joins by naming value; it can still be nested in a layout, because the group communicates through context instead of walking its children.

<ToggleButton.Group value={alignment} onValueChange={setAlignment}>
  <ToggleButton value="start">Start</ToggleButton>
  <ToggleButton value="center">Center</ToggleButton>
  <ToggleButton value="end">End</ToggleButton>
</ToggleButton.Group>

The group is horizontal and wrapping by default. orientation="vertical" makes a column. Its variant, size, radius, color and isDisabled are member defaults; a member can still name its own appearance, while a disabled group always disables every member.

Icon only

<ToggleButton isIconOnly accessibilityLabel="Ajouter aux favoris">
  {({ isSelected }) => (
    <ToggleButton.Icon as={isSelected ? HeartFilled : HeartOutline} />
  )}
</ToggleButton>

isIconOnly removes the horizontal padding and squares the root on its fixed height. An accessible label is required in practice; its absence warns in development.

Style props

Every node accepts its React Native style keys directly, after the recipe and before style:

<ToggleButton width="100%" paddingHorizontal={24}>Pin</ToggleButton>

<ToggleButton>
  <ToggleButton.Label letterSpacing={1}>Pin</ToggleButton.Label>
</ToggleButton>

They are raw React Native values, not hidden token steps. style remains the last word.

Root props

ToggleButtonProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
variantToggleButtonVariant | undefinedNo
sizeSize | undefinedNoHeight, padding, gap, radius and type. Never width.
radiusRadiusKey | undefinedNoOverrides the radius `size` chose.
colorstring | undefinedNoA raw tint (R7). Selection uses its soft slice.
valuestring | undefinedNoThe value this button represents inside a `ToggleButton.Group`.
isSelectedboolean | undefinedNoControlled selection. Leave it out and the button keeps its own state.
defaultSelectedboolean | undefinedNoThe starting selection when uncontrolled.
onSelectedChange((isSelected: boolean) => void) | undefinedNoFired with the next value after every press, controlled or not.
isDisabledboolean | undefinedNo
isIconOnlyboolean | undefinedNoDrops the horizontal padding and squares the button on its fixed height.
asChildboolean | undefinedNoR12 — merge into the single child instead of rendering a pressable.
styleStyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>)NoR9 — `Pressable`'s function form as much as an object or an array.
childrenReactNode | ((state: ToggleButtonRenderState) => ReactNode)No

Slots

ToggleButtonGroupProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
valuestring | undefinedNoThe selected button's value. Leave it out for an uncontrolled group.
defaultValuestring | undefinedNoThe selected button on first mount when the group is uncontrolled.
onValueChange((value: string) => void) | undefinedNoFires when a button selects itself. A group always has zero or one selection.
orientationToggleButtonGroupOrientation | undefinedNo
variantToggleButtonVariant | undefinedNoDefaults passed to every member; a member's own appearance still wins.
sizeSize | undefinedNo
radiusRadiusKey | undefinedNo
colorstring | undefinedNo
isDisabledboolean | undefinedNoStops every member. A member cannot opt back in.
styleStyleProp<ViewStyle>No
childrenReactNodeNo

ToggleButtonIconProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

This slot adds nothing to the props of its React Native node.

ToggleButtonLabelProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

Variants, sizes and colour

Variants

variantResting backgroundResting contentSelected backgroundSelected content
primarydefaultdefaultForegroundaccentaccentForeground
secondarydefaultSoftdefaultSoftForegroundaccentSoftaccentSoftForeground
ghosttransparentforegroundtransparentaccent

primary is the filled default. secondary keeps a softer fill both at rest and when selected. ghost never paints a background; selection changes only its text and icon colour. color replaces the relevant family through the tint pass rather than entering the style cache.

Accessibility

  • The root has accessibilityRole="button" by default and remains overridable.
  • accessibilityState.selected always reflects the current value and is merged with state supplied by the caller.
  • aria-pressed mirrors the same value for React Native Web.
  • In a group, members use the radio role and aria-checked; the group is announced as a radiogroup.
  • accessibilityState.disabled follows isDisabled.
  • An icon-only root warns without accessibilityLabel or aria-label.
  • Press handlers are composed: the toggle, onPress and the internal pressed state all survive together.

Migration from legacy

There is no legacy ToggleButton; this is a net-new v1 component. A legacy Button that manually switched its appearance can move its boolean directly:

BeforeAfter
<Button onPress={() => setLiked(!liked)}><ToggleButton isSelected={liked} onSelectedChange={setLiked}>
conditional customAppearanceselected styling from the recipe
conditional startContentrender child + ToggleButton.Icon