Radio Group
A group of mutually exclusive radio options.
Radio Group is the React Native implementation shipped by the native-radio-group registry item. The component adapts the registry's web-facing composition to native primitives and touch-based interaction.
The native RadioGroup provides a simple controlled or uncontrolled selection model for mutually exclusive options. RadioGroupItem reads and updates the value through context, so each item only needs a unique value.
The component uses native touchable controls and exposes radio accessibility state for each option.
See the shadcn/ui Radio Group documentation for the web component API, concepts, and additional examples.
Installation
CLI
npx shadcn add https://ui.tiesen.id.vn/r/native-radio-group.jsonManual
Copy and paste the following code into your project.
import * as React from 'react'import { TouchableOpacity, View } from 'react-native'import { cn } from '@/lib/utils'interface RadioGroupContextValue { value: string onValueChange: (value: string) => void}const RadioGroupContext = React.createContext<RadioGroupContextValue | null>( null)const useRadioGroupContext = () => { const context = React.use(RadioGroupContext) if (!context) throw new Error('RadioGroup components must be wrapped in <RadioGroup />') return context}function RadioGroup({ className, value: valueProp, onValueChange, children, ...props}: React.ComponentProps<typeof View> & { value?: string onValueChange?: (value: string) => void}) { const [internalValue, setInternalValue] = React.useState(valueProp ?? '') const isControlled = valueProp !== undefined && onValueChange !== undefined const value = isControlled ? valueProp : internalValue const handleValueChange = React.useCallback( (val: string) => { if (!isControlled) setInternalValue(val) onValueChange?.(val) }, [isControlled, onValueChange] ) const memoizedValue = React.useMemo( () => ({ value, onValueChange: handleValueChange }), [value, handleValueChange] ) return ( <RadioGroupContext value={memoizedValue}> <View data-slot='radio-group' accessibilityRole='radiogroup' className={cn('flex w-full flex-col gap-3', className)} {...props} > {children} </View> </RadioGroupContext> )}function RadioGroupItem({ className, value: itemValue, disabled = false, activeOpacity = 0.8, onPress, children, ...props}: React.ComponentProps<typeof TouchableOpacity> & { value: string}) { const { value, onValueChange } = useRadioGroupContext() const checked = value === itemValue const handlePress = React.useCallback( (event: Parameters<NonNullable<typeof onPress>>[0]) => { if (disabled) return onValueChange(itemValue) onPress?.(event) }, [disabled, itemValue, onValueChange, onPress] ) return ( <TouchableOpacity data-slot='radio-group-item' accessibilityRole='radio' accessibilityState={{ checked, disabled }} aria-checked={checked} aria-disabled={disabled} disabled={disabled} onPress={handlePress} activeOpacity={activeOpacity} className={cn( 'flex flex-row items-center gap-2 py-1', disabled && 'opacity-50', className )} {...props} > <View className={cn( 'relative flex size-5 shrink-0 items-center justify-center rounded-full border border-border bg-transparent transition-colors', checked && 'border-primary' )} > {checked && ( <View data-slot='radio-group-indicator' className='size-2 rounded-full bg-primary' /> )} </View> {children} </TouchableOpacity> )}export { RadioGroup, RadioGroupItem }Features
- Single selected value.
- Controlled and uncontrolled group state.
- Mutually exclusive radio items.
- Disabled item support.
- Native radio accessibility semantics.
- Optional children for labels or custom content.
Usage
import { RadioGroup, RadioGroupItem } from '@/components/native/radio-group'
export function RadioGroupExample() {
const [value, setValue] = React.useState('system')
return (
<RadioGroup value={value} onValueChange={setValue}>
<RadioGroupItem value='light'>
Light
</RadioGroupItem>
<RadioGroupItem value='dark'>
Dark
</RadioGroupItem>
<RadioGroupItem value='system'>
System
</RadioGroupItem>
</RadioGroup>
)
}Uncontrolled
<RadioGroup>
<RadioGroupItem value='small'>Small</RadioGroupItem>
<RadioGroupItem value='large'>Large</RadioGroupItem>
</RadioGroup>The group stores the selected value internally when value and onValueChange are not both supplied.
Controlled
<RadioGroup
value={size}
onValueChange={setSize}
>
<RadioGroupItem value='small'>Small</RadioGroupItem>
<RadioGroupItem value='large'>Large</RadioGroupItem>
</RadioGroup>The group is controlled when both props are supplied.
Disabled options
<RadioGroupItem value='legacy' disabled>
Legacy
</RadioGroupItem>A disabled item does not update the selected value.
API Reference
RadioGroup
| Prop | Type | Default |
|---|---|---|
value | string | "" when uncontrolled |
onValueChange | (value: string) => void | — |
className | string | — |
children | React.ReactNode | — |
...props | React.ComponentProps<typeof View> | — |
RadioGroupItem
| Prop | Type | Default |
|---|---|---|
value | string | required |
disabled | boolean | false |
activeOpacity | number | 0.8 |
onPress | TouchableOpacityProps['onPress'] | — |
children | React.ReactNode | — |
className | string | — |
...props | React.ComponentProps<typeof TouchableOpacity> | — |
RadioGroupItem must be rendered inside RadioGroup.