# Radio

**📖 Live documentation:** https://cds.coinbase.com/components/inputs/Radio/?platform=mobile

Radio is a control component that allows users to select one option from a set of mutually exclusive options.

## Import

```tsx
import { Radio } from '@coinbase/cds-mobile/controls/Radio'
```

## Examples

### Basic Usage

Radio components are typically used individually as part of a radio group. Each radio represents a single option in a mutually exclusive set.

```jsx
function BasicRadio() {
  const [selectedValue, setSelectedValue] = useState('option1');

  return (
    <VStack gap={2}>
      <Radio
        name="basic-radio"
        value="option1"
        checked={selectedValue === 'option1'}
        onChange={(value) => setSelectedValue(value)}
      >
        Option 1
      </Radio>
      <Radio
        name="basic-radio"
        value="option2"
        checked={selectedValue === 'option2'}
        onChange={(value) => setSelectedValue(value)}
      >
        Option 2
      </Radio>
      <Radio
        name="basic-radio"
        value="option3"
        checked={selectedValue === 'option3'}
        onChange={(value) => setSelectedValue(value)}
      >
        Option 3
      </Radio>
    </VStack>
  );
}
```

### Radio Groups

The recommended way to use Radio components is with a ControlGroup for better accessibility and easier state management.

```jsx
function RadioGroupExample() {
  const options = [
    { value: 'btc', children: 'Bitcoin' },
    { value: 'eth', children: 'Ethereum' },
    { value: 'ltc', children: 'Litecoin' },
  ];

  const [selectedCurrency, setSelectedCurrency] = useState('btc');

  return (
    <ControlGroup
      accessibilityRole="radiogroup"
      ControlComponent={Radio}
      label="Choose a cryptocurrency"
      options={options}
      value={selectedCurrency}
      onChange={setSelectedCurrency}
      name="currency-radio-group"
    />
  );
}
```

### Custom Colors

You can customize the radio's color using the `controlColor` prop.

```jsx
function CustomColorRadio() {
  const [selectedColor, setSelectedColor] = useState('default');

  return (
    <VStack gap={2}>
      <Radio
        name="color-radio"
        value="default"
        checked={selectedColor === 'default'}
        onChange={(value) => setSelectedColor(value)}
      >
        Default Color
      </Radio>
      <Radio
        name="color-radio"
        value="green"
        checked={selectedColor === 'green'}
        onChange={(value) => setSelectedColor(value)}
        controlColor="accentBoldGreen"
      >
        Custom Green
      </Radio>
      <Radio
        name="color-radio"
        value="purple"
        checked={selectedColor === 'purple'}
        onChange={(value) => setSelectedColor(value)}
        controlColor="accentBoldPurple"
      >
        Custom Purple
      </Radio>
    </VStack>
  );
}
```

### Disabled State

Radio components can be disabled to prevent user interaction.

```jsx
function DisabledRadio() {
  const [selectedValue, setSelectedValue] = useState('enabled');

  return (
    <VStack gap={2}>
      <Radio
        name="disabled-radio"
        value="enabled"
        checked={selectedValue === 'enabled'}
        onChange={(value) => setSelectedValue(value)}
      >
        Enabled Radio
      </Radio>
      <Radio
        name="disabled-radio"
        value="disabled-unchecked"
        checked={false}
        disabled
        onChange={(value) => setSelectedValue(value)}
      >
        Disabled & Unchecked
      </Radio>
      <Radio
        name="disabled-radio"
        value="disabled-checked"
        checked={true}
        disabled
        onChange={(value) => setSelectedValue(value)}
      >
        Disabled & Checked
      </Radio>
    </VStack>
  );
}
```

### Accessibility

Radio components on mobile should always include proper accessibility labels and hints.

```jsx
function AccessibleRadio() {
  const [selectedPayment, setSelectedPayment] = useState('credit');

  return (
    <VStack gap={2}>
      <Radio
        name="payment-radio"
        value="credit"
        checked={selectedPayment === 'credit'}
        onChange={(value) => setSelectedPayment(value)}
        accessibilityLabel="Credit Card"
        accessibilityHint="Pay with credit card"
      >
        Credit Card
      </Radio>
      <Radio
        name="payment-radio"
        value="debit"
        checked={selectedPayment === 'debit'}
        onChange={(value) => setSelectedPayment(value)}
        accessibilityLabel="Debit Card"
        accessibilityHint="Pay with debit card"
      >
        Debit Card
      </Radio>
      <Radio
        name="payment-radio"
        value="crypto"
        checked={selectedPayment === 'crypto'}
        onChange={(value) => setSelectedPayment(value)}
        accessibilityLabel="Cryptocurrency"
        accessibilityHint="Pay with cryptocurrency"
      >
        Cryptocurrency
      </Radio>
    </VStack>
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `bg` | Background color of the overlay (element being interacted with). |
| `borderColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `checked ? controlColor : 'bgLineHeavy'` | - |
| `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `1000` | - |
| `borderWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `100` | Sets the border width of the radio. |
| `checked` | `boolean` | No | `-` | Set the control to selected/on. |
| `children` | `null \| string \| number \| bigint \| false \| true \| ReactElement<unknown, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| Promise<AwaitedReactNode>` | No | `-` | Label for the control option. |
| `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `black` | - |
| `controlColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `bgPrimary` | Sets the checked/active color of the radio. |
| `controlSize` | `number` | No | `theme.controlSize.radioSize` | Sets the outer radio control size in pixels. |
| `disabled` | `boolean` | No | `-` | Disable user interaction. |
| `dotSize` | `number` | No | `2/3 of controlSize` | Sets the inner dot size in pixels. |
| `elevation` | `0 \| 1 \| 2` | No | `-` | Sets the elevation/drop shadow of the control. |
| `indeterminate` | `boolean` | No | `-` | Enable indeterminate state. Useful when you want to indicate that sub-items of a control are partially filled. |
| `onChange` | `((value: RadioValue, checked?: boolean \| undefined) => void) \| undefined` | No | `-` | Toggle control selected state. |
| `onPointerCancel` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerCancelCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerDown` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerDownCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerEnter` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerEnterCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerLeave` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerLeaveCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerMove` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerMoveCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerUp` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerUpCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPress` | `((event: GestureResponderEvent) => void) \| null` | No | `-` | Called when a single tap gesture is detected. |
| `readOnly` | `boolean` | No | `-` | Set the control to ready-only. Similar effect as disabled. |
| `ref` | `null \| RefObject<View \| null> \| (instance: View \| null) => void \| (() => VoidOrUndefinedOnly)` | No | `-` | - |
| `style` | `ViewStyle` | No | `-` | - |
| `testID` | `string` | No | `-` | Used to locate this view in end-to-end tests. Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
| `value` | `string` | No | `-` | Value of the option. Useful for multiple choice. |


