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.
| Prop | Type | Required | Description |
|---|---|---|---|
| variant | ToggleButtonVariant | undefined | No | — |
| size | Size | undefined | No | Height, padding, gap, radius and type. Never width. |
| radius | RadiusKey | undefined | No | Overrides the radius `size` chose. |
| color | string | undefined | No | A raw tint (R7). Selection uses its soft slice. |
| value | string | undefined | No | The value this button represents inside a `ToggleButton.Group`. |
| isSelected | boolean | undefined | No | Controlled selection. Leave it out and the button keeps its own state. |
| defaultSelected | boolean | undefined | No | The starting selection when uncontrolled. |
| onSelectedChange | ((isSelected: boolean) => void) | undefined | No | Fired with the next value after every press, controlled or not. |
| isDisabled | boolean | undefined | No | — |
| isIconOnly | boolean | undefined | No | Drops the horizontal padding and squares the button on its fixed height. |
| asChild | boolean | undefined | No | R12 — merge into the single child instead of rendering a pressable. |
| style | StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>) | No | R9 — `Pressable`'s function form as much as an object or an array. |
| children | ReactNode | ((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.
| Prop | Type | Required | Description |
|---|---|---|---|
| value | string | undefined | No | The selected button's value. Leave it out for an uncontrolled group. |
| defaultValue | string | undefined | No | The selected button on first mount when the group is uncontrolled. |
| onValueChange | ((value: string) => void) | undefined | No | Fires when a button selects itself. A group always has zero or one selection. |
| orientation | ToggleButtonGroupOrientation | undefined | No | — |
| variant | ToggleButtonVariant | undefined | No | Defaults passed to every member; a member's own appearance still wins. |
| size | Size | undefined | No | — |
| radius | RadiusKey | undefined | No | — |
| color | string | undefined | No | — |
| isDisabled | boolean | undefined | No | Stops every member. A member cannot opt back in. |
| style | StyleProp<ViewStyle> | No | — |
| children | ReactNode | No | — |
ToggleButtonIconProps
The node's inherited React Native props apply too, and so do its style props — padding, margin, width and the rest.
ToggleButtonLabelProps
The node's inherited React Native props apply too, and so do its style props — padding, margin, width and the rest.
| Prop | Type | Required | Description |
|---|---|---|---|
| children | ReactNode | No | — |
Variants, sizes and colour
Variants
variant | Resting background | Resting content | Selected background | Selected content |
|---|---|---|---|---|
primary | default | defaultForeground | accent | accentForeground |
secondary | defaultSoft | defaultSoftForeground | accentSoft | accentSoftForeground |
ghost | transparent | foreground | transparent | accent |
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.selectedalways reflects the current value and is merged with state supplied by the caller.aria-pressedmirrors the same value for React Native Web.- In a group, members use the
radiorole andaria-checked; the group is announced as aradiogroup. accessibilityState.disabledfollowsisDisabled.- An icon-only root warns without
accessibilityLabeloraria-label. - Press handlers are composed: the toggle,
onPressand 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:
| Before | After |
|---|---|
<Button onPress={() => setLiked(!liked)}> | <ToggleButton isSelected={liked} onSelectedChange={setLiked}> |
conditional customAppearance | selected styling from the recipe |
conditional startContent | render child + ToggleButton.Icon |