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 { 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>
)
}
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>
Unique identifier for the radio button element
Name attribute that groups radio buttons together. All radios in a group should have the same name
The value attribute used when submitting the form
onChange
React.ChangeEventHandler<HTMLInputElement>
required
Callback fired when the radio button is selected
If true, the radio button is selected
Text that appears to the right of the radio button
If true, the radio button is disabled and cannot be selected
If true, the radio button is marked as required in a form
Accessibility label for screen readers
Brand theme to apply (avon, natura, theBodyShop, etc.)
Optional CSS class name for custom styling
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
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>
)
}
Related Components
- Checkbox - For multiple selections
- Switch - For binary on/off toggles
- Select - For single selection from many options