RadioGroup

Use in a form to allow selecting one option from multiple

RadioGroup lets users choose one option from a keyboard-navigable set on web and native.

Features

  • Accessible, composable, and customizable

  • Sizing with controlled or uncontrolled state

  • Optional native HTML radio buttons on web

Installation

RadioGroup is already installed in tamagui, or you can install it independently:

yarn add @tamagui/radio-group

Usage

import { RadioGroup } from 'tamagui'
export default () => (
<RadioGroup value="foo" gap="2">
<RadioGroup.Item value="foo" id="foo-radio-item">
<RadioGroup.Indicator />
</RadioGroup.Item>
<RadioGroup.Item value="bar" id="bar-radio-item">
<RadioGroup.Indicator />
</RadioGroup.Item>
</RadioGroup>
)

Headless usage

To use headless without @tamagui/core, install @tamagui/radio-headless:

yarn add @tamagui/radio-headless

The @tamagui/radio-headless package provides three hooks for building custom radio groups:

  • useRadioGroup - for the container/group
  • useRadioGroupItem - for individual radio items
  • useRadioGroupItemIndicator - for the indicator (checked state visual)
import {
useRadioGroup,
useRadioGroupItem,
useRadioGroupItemIndicator,
RadioGroupContextValue,
RadioGroupItemContextValue,
} from '@tamagui/radio-headless'
import { createContext, useContext } from 'react'
// Create contexts for the group and item
const RadioGroupContext = createContext<RadioGroupContextValue>({})
const RadioGroupItemContext = createContext<RadioGroupItemContextValue>({
checked: false,
})
function RadioGroup({ children, ...props }) {
const { providerValue, frameAttrs } = useRadioGroup({
orientation: 'vertical',
...props,
})
return (
<RadioGroupContext.Provider value={providerValue}>
<div {...frameAttrs} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{children}
</div>
</RadioGroupContext.Provider>
)
}
function RadioItem({ value, children }) {
const { checked, providerValue, frameAttrs, bubbleInput } = useRadioGroupItem({
radioGroupContext: RadioGroupContext,
value,
})
return (
<RadioGroupItemContext.Provider value={providerValue}>
<button {...frameAttrs} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: 8, border: '1px solid #ccc', borderRadius: 4, background: checked ? '#e0f2fe' : 'white', }} >
<RadioIndicator />
{children}
</button>
{bubbleInput}
</RadioGroupItemContext.Provider>
)
}
function RadioIndicator() {
const { checked, ...indicatorProps } = useRadioGroupItemIndicator({
radioGroupItemContext: RadioGroupItemContext,
})
return (
<div {...indicatorProps} style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid #3b82f6', display: 'flex', alignItems: 'center', justifyContent: 'center', }} >
{checked && (
<div style={{ width: 8, height: 8, borderRadius: '50%', background: '#3b82f6' }} />
)}
</div>
)
}
// Usage
function App() {
return (
<RadioGroup defaultValue="option1" onValueChange={console.log}>
<RadioItem value="option1">Option 1</RadioItem>
<RadioItem value="option2">Option 2</RadioItem>
<RadioItem value="option3">Option 3</RadioItem>
</RadioGroup>
)
}

API reference

RadioGroup

RadioGroup extends View, inheriting all the Tamagui standard props, plus:

Props

  • name

    string

    Equivalent to input name attribute.

  • value

    string

    Controlled value of the selected radio item.

  • defaultValue

    string

    Default value for uncontrolled usage.

  • required

    boolean

    Sets aria-required attribute.

  • disabled

    boolean

    Sets aria-disabled on web, and disables touch on native for all children items.

  • native

    boolean

    Default: 

    false

    Renders native radio button on web.

  • onValueChange

    (value: string) => void

    Called when the selected value changes.

  • orientation

    "horizontal" | "vertical"

    Orientation of the radio group.

  • accentColor

    string

    Sets `accent-color` style when `native` prop is enabled.

  • RadioGroup.Item

    Item sizes come from the shared Sizing ladder.

    Props

  • size

    'xs' | 'sm' | 'md' | 'lg' | 'xl'

    A named size ("xs" to "xl", default "md") is a rung in the sizing ladder: the circle is the ladder's square derivation.

  • labeledBy

    string

    Sets aria-labelledby on web.

  • value

    string

    Input value for the radio button.

  • disabled

    boolean

    Sets aria-disabled on web, and disables touch on native.

  • id

    string

    ID used on the web.

  • unstyled

    boolean

    Default: 

    false

    When true, removes all default Tamagui styling.

  • RadioGroup.Indicator

    RadioGroup.Indicator appears only when the parent Item is checked. It extends YStack, getting Tamagui standard props adding:

    Props

  • unstyled

    boolean

    Removes all default Tamagui styles.

  • Headless props

    useRadioGroup return value

    PropertyTypeDescription
    providerValueRadioGroupContextValueValue to pass to your context provider
    frameAttrsobjectProps to spread on the group container (role, aria-orientation, etc.)
    rovingFocusGroupAttrsobjectProps for roving focus behavior (optional)
    Property
    providerValue
    Type
    RadioGroupContextValue
    Description
    Value to pass to your context provider
    Property
    frameAttrs
    Type
    object
    Description
    Props to spread on the group container (role, aria-orientation, etc.)
    Property
    rovingFocusGroupAttrs
    Type
    object
    Description
    Props for roving focus behavior (optional)

    useRadioGroupItem

    Hook for individual radio items. Requires a context containing the group state.

    const {
    checked,
    isFormControl,
    providerValue,
    bubbleInput,
    native,
    frameAttrs,
    rovingFocusGroupAttrs,
    } = useRadioGroupItem({
    radioGroupContext: YourRadioGroupContext,
    value: 'option1',
    id: 'option1-id',
    labelledBy: 'label-id',
    disabled: false,
    onPress: () => {},
    onKeyDown: () => {},
    onFocus: () => {},
    })
    PropertyTypeDescription
    checkedbooleanWhether this item is currently selected
    providerValueobjectContext value for the indicator
    frameAttrsobjectProps to spread on the item element
    bubbleInputReactNodeHidden input for form compatibility
    nativebooleanWhether using native radio
    isFormControlbooleanWhether inside a form
    Property
    checked
    Type
    boolean
    Description
    Whether this item is currently selected
    Property
    providerValue
    Type
    object
    Description
    Context value for the indicator
    Property
    frameAttrs
    Type
    object
    Description
    Props to spread on the item element
    Property
    bubbleInput
    Type
    ReactNode
    Description
    Hidden input for form compatibility
    Property
    native
    Type
    boolean
    Description
    Whether using native radio
    Property
    isFormControl
    Type
    boolean
    Description
    Whether inside a form

    useRadioGroupItemIndicator

    Hook for the visual indicator of checked state.

    const { checked, ...dataAttrs } = useRadioGroupItemIndicator({
    radioGroupItemContext: YourRadioGroupItemContext,
    disabled: false,
    })
    PropertyTypeDescription
    checkedbooleanWhether the parent item is checked
    data-statestring‘checked’ or ‘unchecked’
    data-disabledstring | undefinedPresent when disabled
    Property
    checked
    Type
    boolean
    Description
    Whether the parent item is checked
    Property
    data-state
    Type
    string
    Description
    ‘checked’ or ‘unchecked’
    Property
    data-disabled
    Type
    string | undefined
    Description
    Present when disabled