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.json

Manual

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

PropTypeDefault
valuestring"" when uncontrolled
onValueChange(value: string) => void—
classNamestring—
childrenReact.ReactNode—
...propsReact.ComponentProps<typeof View>—

RadioGroupItem

PropTypeDefault
valuestringrequired
disabledbooleanfalse
activeOpacitynumber0.8
onPressTouchableOpacityProps['onPress']—
childrenReact.ReactNode—
classNamestring—
...propsReact.ComponentProps<typeof TouchableOpacity>—

RadioGroupItem must be rendered inside RadioGroup.

On this page