Skip to main content

Overview

The RadioButton component (also called Radio) enables users to select exactly one option from a group of choices. Unlike checkboxes, radio buttons in a group are mutually exclusive.

Import

import { RadioButton } from '@naturacosmeticos/natds-react'

Basic Usage

import React, { useState } from 'react'
import { RadioButton } from '@naturacosmeticos/natds-react'

function App() {
  const [selected, setSelected] = useState('option1')

  return (
    <div>
      <RadioButton
        id="option1"
        name="choice"
        label="Option 1"
        value="option1"
        checked={selected === 'option1'}
        onChange={(e) => setSelected(e.target.value)}
      />
      <RadioButton
        id="option2"
        name="choice"
        label="Option 2"
        value="option2"
        checked={selected === 'option2'}
        onChange={(e) => setSelected(e.target.value)}
      />
    </div>
  )
}

Radio Group

Radio buttons should always be used in groups with the same name attribute:
import React, { useState } from 'react'
import { RadioButton } from '@naturacosmeticos/natds-react'

function ShippingOptions() {
  const [shipping, setShipping] = useState('standard')

  return (
    <fieldset>
      <legend>Select shipping method</legend>
      
      <RadioButton
        id="standard"
        name="shipping"
        label="Standard (5-7 days) - Free"
        value="standard"
        checked={shipping === 'standard'}
        onChange={(e) => setShipping(e.target.value)}
      />
      
      <RadioButton
        id="express"
        name="shipping"
        label="Express (2-3 days) - $10"
        value="express"
        checked={shipping === 'express'}
        onChange={(e) => setShipping(e.target.value)}
      />
      
      <RadioButton
        id="overnight"
        name="shipping"
        label="Overnight - $25"
        value="overnight"
        checked={shipping === 'overnight'}
        onChange={(e) => setShipping(e.target.value)}
      />
    </fieldset>
  )
}

States

Checked and Unchecked

const [value, setValue] = useState('option1')

<RadioButton
  id="radio1"
  name="options"
  label="Selected"
  value="option1"
  checked={value === 'option1'}
  onChange={(e) => setValue(e.target.value)}
/>

<RadioButton
  id="radio2"
  name="options"
  label="Unselected"
  value="option2"
  checked={value === 'option2'}
  onChange={(e) => setValue(e.target.value)}
/>

Disabled

<RadioButton
  id="disabled-unchecked"
  name="disabled-group"
  label="Disabled Unchecked"
  value="disabled1"
  checked={false}
  disabled
  onChange={() => {}}
/>

<RadioButton
  id="disabled-checked"
  name="disabled-group"
  label="Disabled Checked"
  value="disabled2"
  checked={true}
  disabled
  onChange={() => {}}
/>

Required

<RadioButton
  id="required-option"
  name="required-group"
  label="This selection is required"
  value="required"
  required
  checked={selected === 'required'}
  onChange={(e) => setSelected(e.target.value)}
/>

With Labels

<div>
  <h3>Choose your plan</h3>
  
  <RadioButton
    id="free"
    name="plan"
    label="Free - $0/month"
    value="free"
    checked={plan === 'free'}
    onChange={(e) => setPlan(e.target.value)}
  />
  
  <RadioButton
    id="pro"
    name="plan"
    label="Pro - $9/month"
    value="pro"
    checked={plan === 'pro'}
    onChange={(e) => setPlan(e.target.value)}
  />
  
  <RadioButton
    id="enterprise"
    name="plan"
    label="Enterprise - Custom pricing"
    value="enterprise"
    checked={plan === 'enterprise'}
    onChange={(e) => setPlan(e.target.value)}
  />
</div>

Props

id
string
required
Unique identifier for the radio button element
name
string
Name attribute that groups radio buttons together. All radios in a group should have the same name
value
string
required
The value attribute used when submitting the form
onChange
React.ChangeEventHandler<HTMLInputElement>
required
Callback fired when the radio button is selected
checked
boolean
default:"false"
If true, the radio button is selected
label
string
Text that appears to the right of the radio button
disabled
boolean
default:"false"
If true, the radio button is disabled and cannot be selected
required
boolean
default:"false"
If true, the radio button is marked as required in a form
ariaLabel
string
Accessibility label for screen readers
brand
BrandTypes
Brand theme to apply (avon, natura, theBodyShop, etc.)
className
string
Optional CSS class name for custom styling
testID
string
Optional ID for testing purposes. Defaults to ds-RadioButton-{id}
onBlur
React.ChangeEventHandler<HTMLInputElement>
Callback fired when the radio button loses focus
onFocus
React.ChangeEventHandler<HTMLInputElement>
Callback fired when the radio button gains focus
accessibility
AriaAttributes
Additional ARIA attributes for enhanced accessibility

Accessibility

Built-in Features

The RadioButton component includes:
  • Keyboard navigation (Arrow keys to move between options, Space to select)
  • Focus indicators with ripple effect
  • Proper ARIA attributes
  • Label association
  • Screen reader support

Best Practices

// Good: Use fieldset and legend for radio groups
<fieldset>
  <legend>Select your preferred contact method</legend>
  <RadioButton
    id="email"
    name="contact"
    label="Email"
    value="email"
    checked={contact === 'email'}
    onChange={(e) => setContact(e.target.value)}
  />
  <RadioButton
    id="phone"
    name="contact"
    label="Phone"
    value="phone"
    checked={contact === 'phone'}
    onChange={(e) => setContact(e.target.value)}
  />
</fieldset>

// Good: Provide clear labels
<RadioButton
  id="yes"
  name="consent"
  label="Yes, I agree to receive promotional emails"
  value="yes"
  checked={consent === 'yes'}
  onChange={(e) => setConsent(e.target.value)}
/>

Guidelines

  • Always group related radio buttons with the same name
  • Provide 2-5 options (use Select for more options)
  • Use clear, descriptive labels
  • Wrap groups in <fieldset> with <legend>
  • Ensure one option is selected by default when appropriate
  • Provide adequate spacing between options
  • Make touch targets at least 44x44px
  • Use ariaLabel when visual label isn’t sufficient

Form Integration

import React, { useState } from 'react'
import { RadioButton } from '@naturacosmeticos/natds-react'

interface FormData {
  plan: string
  billing: string
}

function SubscriptionForm() {
  const [formData, setFormData] = useState<FormData>({
    plan: 'pro',
    billing: 'monthly'
  })

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    console.log('Selected:', formData)
  }

  return (
    <form onSubmit={handleSubmit}>
      <fieldset>
        <legend>Select Plan</legend>
        <RadioButton
          id="basic"
          name="plan"
          label="Basic - $5/month"
          value="basic"
          checked={formData.plan === 'basic'}
          onChange={(e) => setFormData({ ...formData, plan: e.target.value })}
        />
        <RadioButton
          id="pro"
          name="plan"
          label="Pro - $9/month"
          value="pro"
          checked={formData.plan === 'pro'}
          onChange={(e) => setFormData({ ...formData, plan: e.target.value })}
        />
      </fieldset>

      <fieldset>
        <legend>Billing Cycle</legend>
        <RadioButton
          id="monthly"
          name="billing"
          label="Monthly"
          value="monthly"
          checked={formData.billing === 'monthly'}
          onChange={(e) => setFormData({ ...formData, billing: e.target.value })}
        />
        <RadioButton
          id="annual"
          name="billing"
          label="Annual (Save 20%)"
          value="annual"
          checked={formData.billing === 'annual'}
          onChange={(e) => setFormData({ ...formData, billing: e.target.value })}
        />
      </fieldset>

      <button type="submit">Subscribe</button>
    </form>
  )
}

TypeScript

import { RadioButtonProps } from '@naturacosmeticos/natds-react'

interface RadioOption {
  id: string
  label: string
  value: string
  disabled?: boolean
}

interface RadioGroupProps {
  name: string
  options: RadioOption[]
  value: string
  onChange: (value: string) => void
}

const RadioGroup: React.FC<RadioGroupProps> = ({ name, options, value, onChange }) => {
  return (
    <div role="radiogroup">
      {options.map(option => (
        <RadioButton
          key={option.id}
          id={option.id}
          name={name}
          label={option.label}
          value={option.value}
          checked={value === option.value}
          disabled={option.disabled}
          onChange={(e) => onChange(e.target.value)}
        />
      ))}
    </div>
  )
}
  • Checkbox - For multiple selections
  • Switch - For binary on/off toggles
  • Select - For single selection from many options

Build docs developers (and LLMs) love