Radio button components

FREE

Free and open-source React radio button components built for modern applications and websites. These radio buttons are built using React Aria and styled with Tailwind CSS.

Installation

You can add this radio button component using our CLI or manually:

npx untitledui@latest add radio-buttons

Radio button examples

Below are examples and variations of this radio button component:

API reference

RadioButton

Import RadioButton from @/components/base/radio-buttons/radio-buttons. Renders React Aria’s Radio — all of its props are supported.

RadioButton props
PropTypeDefaultDescription
size
"sm""md"
"sm"Controls the circle size, the gap between it and the text, and the label and hint typography. Ignored when a surrounding RadioGroup sets a size.
labelReactNode—Content rendered beside the circle. It sits inside the radio's label element, so it also acts as the accessible name and selects the radio when clicked.
hintReactNode—Supporting text rendered under the label. Clicks on it are stopped so reading the hint does not select the radio.
Props from React Aria (28)
RadioButton inherited props
PropTypeDefaultDescription
onClick((e: MouseEvent<FocusableElement>) => void)—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.
onPress((e: PressEvent) => void)—Handler that is called when the press is released over the target.
onPressChange((isPressed: boolean) => void)—Handler that is called when the press state changes.
onPressEnd((e: PressEvent) => void)—Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressStart((e: PressEvent) => void)—Handler that is called when a press interaction starts.
onPressUp((e: PressEvent) => void)—Handler that is called when a press is released over the target, regardless of whether it started on the target or not.
onHoverChange((isHovering: boolean) => void)—Handler that is called when the hover state changes.
onHoverEnd((e: HoverEvent) => void)—Handler that is called when a hover interaction ends.
onHoverStart((e: HoverEvent) => void)—Handler that is called when a hover interaction starts.
onBlur((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element loses focus.
onFocus((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element receives focus.
onFocusChange((isFocused: boolean) => void)—Handler that is called when the element's focus status changes.
autoFocusboolean—Whether the element should receive focus on render.
onKeyDown((e: KeyboardEvent) => void)—Handler that is called when a key is pressed.
onKeyUp((e: KeyboardEvent) => void)—Handler that is called when a key is released.
idstring—The element's unique identifier.
inputRefRef<HTMLInputElement | null>—A ref for the HTML input element.
isDisabledboolean—Whether the radio button is disabled or not. Shows that a selection exists, but is not available in that circumstance.
value* (required)string—The value of the radio button, used when submitting an HTML form.
styleStyleOrFunction<RadioRenderProps>—The inline style for the element. A function may be provided to compute the style based on component state.
childrenChildrenOrFunction<RadioRenderProps>—The children of the component. A function may be provided to alter the children based on component state.
slotstring | null—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.
classNameClassNameOrFunction<RadioRenderProps>—The CSS className for the element. A function may be provided to compute the class based on component state.
renderDOMRenderFunction<"label", RadioRenderProps>—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.
aria-describedbystring—Identifies the element (or elements) that describes the object.
aria-detailsstring—Identifies the element (or elements) that provide a detailed, extended description for the object.
aria-labelstring—Defines a string value that labels the current element.
aria-labelledbystring—Identifies the element (or elements) that labels the current element.

Also accepts the global HTML attributes (dir, lang, hidden, inert, translate), all standard React DOM event handlers.

RadioButtonBase

Import RadioButtonBase from @/components/base/radio-buttons/radio-buttons. Renders a <div> element.

RadioButtonBase props
PropTypeDefaultDescription
size
"sm""md"
"sm"Controls the diameter of the circle and of the dot rendered inside it.
classNamestring—Additional classes merged onto the circle element.
isFocusVisibleboolean—Whether to draw the keyboard focus ring around the circle. Normally passed down by RadioButton from React Aria's render state rather than set by hand.
isSelectedboolean—Whether to render the checked appearance: brand-filled circle with the inner dot faded in. Normally passed down by RadioButton rather than set by hand.
isDisabledboolean—Whether to render the dimmed, not-allowed appearance. Normally passed down by RadioButton rather than set by hand.

RadioGroup

Import RadioGroup from @/components/base/radio-buttons/radio-buttons. Renders React Aria’s RadioGroup — all of its props are supported.

RadioGroup props
PropTypeDefaultDescription
children* (required)ReactNode—The RadioButton elements belonging to the group, stacked in a vertical column.
classNamestring—Additional classes merged onto the group wrapper that lays the radios out.
size
"sm""md"
"sm"Size applied to every RadioButton inside the group through context, taking precedence over a size set on the individual radio.
Props from React Aria (24)
RadioGroup inherited props
PropTypeDefaultDescription
onBlur((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element loses focus.
onFocus((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element receives focus.
onFocusChange((isFocused: boolean) => void)—Handler that is called when the element's focus status changes.
formstring—The <form> element to associate the input with. The value of this attribute must be the id of a <form> in the same document.
namestring—The name of the input element, used when submitting an HTML form.
defaultValuestring | null—The default value (uncontrolled).
idstring—The element's unique identifier.
isDisabledboolean—Whether the input is disabled.
isInvalidboolean—Whether the input value is invalid.
isReadOnlyboolean—Whether the input can be selected but not changed by the user.
isRequiredboolean—Whether user input is required on the input before form submission.
onChange((value: string) => void)—Handler that is called when the value changes.
orientation
"horizontal""vertical"
"vertical"The axis the Radio Button(s) should align with.
validate((value: string) => ValidationError | true | null | undefined)—A function that returns an error message if a given value is invalid. Validation errors are displayed to the user when the form is submitted if validationBehavior="native". For realtime validation, use the isInvalid prop instead.
validationBehavior
"native""aria"
"native"Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.
valuestring | null—The current value (controlled).
styleStyleOrFunction<RadioGroupRenderProps>—The inline style for the element. A function may be provided to compute the style based on component state.
slotstring | null—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.
renderDOMRenderFunction<"div", RadioGroupRenderProps>—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.
aria-describedbystring—Identifies the element (or elements) that describes the object.
aria-detailsstring—Identifies the element (or elements) that provide a detailed, extended description for the object.
aria-labelstring—Defines a string value that labels the current element.
aria-labelledbystring—Identifies the element (or elements) that labels the current element.
aria-errormessagestring—Identifies the element that provides an error message for the object.

Also accepts the global HTML attributes (dir, lang, hidden, inert, translate), all standard React DOM event handlers.

FAQs

Please refer to our frequently asked questions page for more.

Wrap RadioButton components in a RadioGroup from the same file and give each one a unique value. RadioButton is built on React Aria's Radio, so it must be rendered inside a RadioGroup.

Set defaultValue on RadioGroup for uncontrolled state, or use value with onChange for controlled state. onChange receives the value of the newly selected RadioButton.

Radio buttons support "sm" and "md", and the default is "sm". Set size on RadioGroup; the group's size overrides any size set on an individual RadioButton inside it.

Use the label prop for the main text and the hint prop for supporting text below it. Both accept React nodes. Leave both out to render only the radio circle.

Set isDisabled on RadioGroup to disable every option, or on a single RadioButton to disable only that one. Disabled radios render at 50% opacity with a not-allowed cursor.

RadioGroup doesn't render a visible group label, so pass aria-label as the examples do, or aria-labelledby pointing to your own heading. React Aria handles keyboard support: Tab moves focus into the group and the arrow keys change the selection.

RadioGroup stacks its options with flex flex-col gap-4 by default. Pass className="flex-row" (or any layout classes) to change it. Classes are merged with tailwind-merge, so yours replace the default classes that conflict.