React Native Radio Buttons, Built From Pressable

Core React Native has no radio button component; its user interface list holds only Button and Switch. Build one from Pressable: draw an outer circle and an inner dot, keep the selected value in the parent, and give each option accessibilityRole="radio" plus accessibilityState={{ checked }}. Wrap the set in a View with accessibilityRole="radiogroup".

Accessibility props for a radio group
ElementPropValue
Wrapping ViewaccessibilityRole"radiogroup"
Each optionaccessibilityRole"radio"
Each optionaccessibilityState{ checked: true or false }
Disabled optionaccessibilityState{ disabled: true }, plus the Pressable disabled prop
Newer alternativerole / aria-checkedrole="radio" with aria-checked; role wins over accessibilityRole

Updated

Below is a web version of the same group: it uses the matching ARIA roles, so arrow keys move the choice the way a browser radio set does. Under it, the panel prints the props each option would carry in React Native for the current selection, so you can see what a screen reader is told.

Radio group preview

Delivery method

Click an option or use the arrow keys once the group has focus.

Radio group code

RadioGroup component

Only core components. The parent passes value and onChange, exactly like a controlled TextInput.

import { Pressable, StyleSheet, Text, View } from "react-native";

export function RadioGroup({ label, options, value, onChange }) {
  return (
    <View accessibilityRole="radiogroup" accessibilityLabel={label}>
      <Text style={s.label}>{label}</Text>
      {options.map((o) => {
        const checked = o.value === value;
        return (
          <Pressable
            key={o.value}
            accessibilityRole="radio"
            accessibilityState={{ checked, disabled: !!o.disabled }}
            disabled={o.disabled}
            hitSlop={8}
            onPress={() => onChange(o.value)}
            style={({ pressed }) => [s.row, pressed && { opacity: 0.6 }]}
          >
            <View style={s.ring}>{checked ? <View style={s.dot} /> : null}</View>
            <Text>{o.label}</Text>
          </Pressable>
        );
      })}
    </View>
  );
}

const s = StyleSheet.create({
  label: { fontWeight: "600", marginBottom: 8 },
  row: { flexDirection: "row", alignItems: "center", gap: 10, paddingVertical: 10 },
  ring: { width: 22, height: 22, borderRadius: 11, borderWidth: 2, borderColor: "#2563eb", alignItems: "center", justifyContent: "center" },
  dot: { width: 10, height: 10, borderRadius: 5, backgroundColor: "#2563eb" },
});

Using it in a screen

State lives in the screen, so the choice can be validated and submitted with the rest of a form.

const [delivery, setDelivery] = useState("standard");

<RadioGroup
  label="Delivery method"
  value={delivery}
  onChange={setDelivery}
  options={[
    { value: "standard", label: "Standard shipping" },
    { value: "express", label: "Express shipping" },
    { value: "pickup", label: "Pick up in store", disabled: true },
  ]}
/>

Why build it yourself

A radio group is a small amount of code: a row, two circles and a comparison. Writing it yourself means it matches your design, adds nothing to the bundle and carries exactly the accessibility props you choose.

Keeping the selected value in the parent turns the group into a controlled component, so it slots into a form's values object just like a TextInput does.

Drawing the circle

Use a View with equal width and height, a border and a borderRadius of half the size for the ring. Inside, render a smaller filled View only when the option is selected.

Keep the ring visible in both states and change only the dot. A selection shown by colour alone is hard to see for people with colour blindness.

How it works

  1. The parent holds one value in state; every option compares itself against it.
  2. Pressing an option calls onChange with its own value, which unselects the others automatically.
  3. Each Pressable announces itself as a radio and reports checked through accessibilityState.
  4. The wrapping View is marked as a radiogroup and labelled with the question.

Tips

Frequently asked questions

Is there a RadioButton component in React Native?

No. The official components page lists Button and Switch as the only user interface controls, so radios come from your own Pressable code or from a UI kit you install.

How does a screen reader know my circle is a radio button?

Set accessibilityRole="radio" on the Pressable and accessibilityState={{ checked: isSelected }}. The docs describe the radio role as representing a radio button and radiogroup as a group of them.

Should I use role and aria-checked instead?

Either works. React Native also accepts role="radio" and aria-checked, and the docs say role takes precedence over accessibilityRole when both are set. Pick one style and use it across the app.

Radio buttons or a Switch?

A Switch is for one on and off setting that takes effect at once. Radios are for picking exactly one of several named options.

Related: React Native dropdown, React Native forms, aria-label examples, React Native collapsible sections, React vs React Native.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap