Radio
Accessible radio button component for mutually exclusive selections with keyboard navigation, validation states, and flexible layouts.
Installation
npm install --save @godaddy/antaresProps
childrenReactNode—Label text for the radio button
className?string | undefined'react-aria-Radio'Additional class names applied to the root element.
valuestring—The value of the radio button, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio#Value).
isDisabled?boolean | undefined—Whether the radio button is disabled or not. Shows that a selection exists, but is not available in that circumstance.
autoFocus?boolean | undefined—Whether the element should receive focus on render.
onFocus?((e: React.FocusEvent<Element, Element>) => void) | undefined—Handler that is called when the element receives focus.
onBlur?((e: React.FocusEvent<Element, Element>) => void) | undefined—Handler that is called when the element loses focus.
onFocusChange?((isFocused: boolean) => void) | undefined—Handler that is called when the element's focus status changes.
onKeyDown?((e: KeyboardEvent) => void) | undefined—Handler that is called when a key is pressed.
onKeyUp?((e: KeyboardEvent) => void) | undefined—Handler that is called when a key is released.
id?string | undefined—The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
onPress?((e: PressEvent) => void) | undefined—Handler that is called when the press is released over the target.
onPressStart?((e: PressEvent) => void) | undefined—Handler that is called when a press interaction starts.
onPressEnd?((e: PressEvent) => void) | undefined—Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChange?((isPressed: boolean) => void) | undefined—Handler that is called when the press state changes.
onPressUp?((e: PressEvent) => void) | undefined—Handler that is called when a press is released over the target, regardless of whether it started on the target or not.
onClick?((e: React.MouseEvent<FocusableElement>) => void) | undefined—**Not recommended – use `onPress` instead.** `onClick` is an alias for `onPress` provided for compatibility with other libraries. `onPress` provides additional event details for non-mouse interactions.
inputRef?RefObject<HTMLInputElement | null> | undefined—A ref for the HTML input element.
onHoverStart?((e: HoverEvent) => void) | undefined—Handler that is called when a hover interaction starts.
onHoverEnd?((e: HoverEvent) => void) | undefined—Handler that is called when a hover interaction ends.
onHoverChange?((isHovering: boolean) => void) | undefined—Handler that is called when the hover state changes.
style?StyleOrFunction<RadioRenderProps> | undefined—The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state.
render?DOMRenderFunction<"label", RadioRenderProps> | undefined—Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.
slot?string | null | undefined—A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent.
Examples
Basic
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioBasicExample() { return ( <RadioGroup label="Select your plan" defaultValue="basic"> <Radio value="basic">Basic</Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> );}Controlled
Current selection: standard
import { Radio, RadioGroup } from '@godaddy/antares';import { useState } from 'react';export function RadioControlledExample() { const [selected, setSelected] = useState('standard'); return ( <> <RadioGroup label="Select your plan" value={selected} onChange={setSelected}> <Radio value="basic">Basic</Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> <p>Current selection: {selected}</p> </> );}Horizontal Layout
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioHorizontalExample() { return ( <RadioGroup label="Select your plan" defaultValue="standard" orientation="horizontal"> <Radio value="basic">Basic</Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> );}Disabled States
import { Radio, RadioGroup, Flex } from '@godaddy/antares';export function RadioDisabledExample() { return ( <Flex direction="column" gap="md"> <RadioGroup label="Disabled group" defaultValue="basic" isDisabled> <Radio value="basic">Basic</Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> <RadioGroup label="Individual disabled options" defaultValue="standard"> <Radio value="basic" isDisabled> Basic (disabled) </Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> </Flex> );}Required Indicator
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioRequiredExample() { return ( <RadioGroup label="Payment method" isRequired defaultValue="credit"> <Radio value="credit">Credit Card</Radio> <Radio value="paypal">PayPal</Radio> <Radio value="bank">Bank Transfer</Radio> </RadioGroup> );}Description Text
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioDescriptionExample() { return ( <RadioGroup label="Notification preferences" description="Choose how you'd like to receive updates" defaultValue="email" > <Radio value="email">Email</Radio> <Radio value="sms">SMS</Radio> <Radio value="push">Push Notifications</Radio> </RadioGroup> );}Error State
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioErrorExample() { return ( <RadioGroup label="Select shipping method" isRequired isInvalid errorMessage="Please select a shipping method"> <Radio value="standard">Standard Shipping</Radio> <Radio value="express">Express Shipping</Radio> <Radio value="overnight">Overnight Shipping</Radio> </RadioGroup> );}Aria Label
import { Radio, RadioGroup } from '@godaddy/antares';export function RadioAriaLabelExample() { return ( <RadioGroup aria-label="Sort order" defaultValue="newest"> <Radio value="newest">Newest first</Radio> <Radio value="oldest">Oldest first</Radio> <Radio value="popular">Most popular</Radio> </RadioGroup> );}Form Submission
import { Radio, RadioGroup } from '@godaddy/antares';import { type FormEvent, useState } from 'react';export function RadioFormExample() { const [submittedValue, setSubmittedValue] = useState<string | null>(null); function handleSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); const value = new FormData(event.currentTarget).get('plan'); setSubmittedValue(value != null ? String(value) : null); } return ( <> <form onSubmit={handleSubmit}> <RadioGroup name="plan" label="Select your plan" defaultValue="standard"> <Radio value="basic">Basic</Radio> <Radio value="standard">Standard</Radio> <Radio value="premium">Premium</Radio> </RadioGroup> <button type="submit" style={{ marginTop: '1rem' }}> Submit </button> </form> {submittedValue && <p>Form submitted with value: {submittedValue}</p>} </> );}Customization
Data Attributes
Components automatically add data attributes for styling different states:
RadioGroup Container: data-invalid, data-disabled, data-required, data-readonly, data-orientation
Radio Button: data-selected, data-hovered, data-pressed, data-focused, data-disabled
.my-radio-group [data-selected] {
color: #09757a;
font-weight: 500;
}
.my-radio-group[data-invalid] {
border-color: #db1802;
}
.my-radio-group [data-focused] {
outline: 2px solid #1976d2;
outline-offset: 2px;
}
.my-radio-group [data-disabled] {
opacity: 0.4;
cursor: not-allowed;
}Component Customization
Individual components can be styled by passing className props:
<RadioGroup label="Select plan" className="custom-radio-group">
<Radio value="basic" className="custom-radio">
Basic
</Radio>
<Radio value="premium" className="premium-radio">
Premium
</Radio>
</RadioGroup>Accessibility
Keyboard Navigation
- Tab: Moves focus to/from the radio group
- Arrow Down/Right: Move to next radio button
- Arrow Up/Left: Move to previous radio button
- Space: Select the focused radio button
ARIA Support
role="radiogroup"on the containerrole="radio"on each optionaria-checkedindicates selection statearia-disabledfor disabled optionsaria-requiredwhen selection is requiredaria-invalidfor validation errors
Troubleshooting
Selection Not Updating
// ❌ Wrong: Using both value and defaultValue
<RadioGroup value={value} defaultValue="basic">
<Radio value="basic">Basic</Radio>
</RadioGroup>
// ✅ Controlled mode
<RadioGroup value={value} onChange={setValue}>
<Radio value="basic">Basic</Radio>
</RadioGroup>
// ✅ Uncontrolled mode
<RadioGroup defaultValue="basic">
<Radio value="basic">Basic</Radio>
</RadioGroup>Styling Overrides Not Applying
/* ❌ May not have enough specificity */
.my-custom-radio {
color: red;
}
/* ✅ Use data attributes and className for higher specificity */
.my-radio-group [data-selected] {
color: red;
font-weight: 600;
}Keyboard Navigation Not Working
/* ❌ Don't remove focus outlines without replacement */
[data-focused] {
outline: none;
}
/* ✅ Provide visible focus indicator */
.my-radio-group [data-focused] {
outline: 2px solid #1976d2;
outline-offset: 2px;
}Best Practices
When to Use Radio Buttons
- ✅ When there are 2-7 mutually exclusive options
- ✅ When all options should be visible at once
- ✅ When the decision is important and deserves space
- ❌ For more than 7 options (use Select instead)
- ❌ For binary yes/no choices (use Checkbox or Toggle instead)
- ❌ When space is limited (use Select dropdown)
Label Guidelines
- Use clear, concise labels
- Keep labels short (1-3 words when possible)
- Place the most common option first
- Ensure labels are descriptive without being verbose
Validation
Always provide clear error messages:
<RadioGroup
label="Select shipping method"
isRequired
isInvalid={!selectedShipping}
errorMessage="Please select a shipping method"
>
<Radio value="standard">Standard</Radio>
<Radio value="express">Express</Radio>
</RadioGroup>