Radio Group

A set of options where exactly one can be chosen, as plain radios or as cards.

Delivery speed

import * as React from "react"

import { Radio, RadioGroup, RadioLabel } from "@hel/ui/radio-group"

When to use it

Use a radio group when people must pick one option out of a short list and seeing every option helps them decide: a delivery speed, a billing period, a plan. For a long list, use a Select. For a yes or no choice, use a Switch or a Checkbox.

Arrow keys move between the options, and the one in focus is chosen, the way people expect from native radios.

Import

import { RadioGroup, Radio, RadioLabel, RadioCard } from "@hel/ui/radio-group"

Anatomy

RadioGroup holds the value. Each option is a Radio and a RadioLabel inside one label element, so the whole row can be clicked. Mark the label group so the label text can follow the disabled state of its radio.

<RadioGroup aria-labelledby={labelId} defaultValue="standard">
  <label className="group flex items-center gap-2">
    <Radio value="standard" />
    <RadioLabel title="Standard" />
  </label>
  <label className="group flex items-center gap-2">
    <Radio value="express" />
    <RadioLabel title="Express" />
  </label>
</RadioGroup>

RadioLabel takes a title and an optional description for a second, quieter line. It is the same label as in the Checkbox. When there is a description, align the row to the top with items-start so the radio sits next to the title.

Naming the group

A screen reader needs a name for the whole group, not only for each option. Point aria-labelledby at a heading or text above the group, as the example does, or use aria-label when there is no visible title.

Examples

Horizontal

orientation="horizontal" lays the options out in a row. It only changes the layout. Arrow keys work the same either way.

Billing period

import * as React from "react"

import { Radio, RadioGroup, RadioLabel } from "@hel/ui/radio-group"

Card options

When each option needs room to explain itself, use RadioCard. It is a full width card with a title, a description and a trailing slot at the end for a price or a count. Use it in place of the label and radio pair.

Choose a plan

import * as React from "react"

import { RadioCard, RadioGroup } from "@hel/ui/radio-group"

In a form

Give the group a name and its value is sent with the form. required stops the form from being submitted until an option is chosen.

T-shirt size

import * as React from "react"

import { Button } from "@hel/ui/button"

Disabled and invalid

disabled on a Radio turns off one option, and its title and description are dimmed. Put disabled on the group to turn off all of them. readOnly keeps the current choice visible but locked.

Inside an invalid Field, every radio shows the error border and ring. Add a FieldHelperText to say what is wrong.

Seat

Notification channel

Pick a channel to continue.

import * as React from "react"

import { Field, FieldHelperText } from "@hel/ui/field"

Controlled value

Pass value and onValueChange to control the group yourself. The value is the value of the chosen Radio.

const [plan, setPlan] = React.useState("pro")
 
<RadioGroup value={plan} onValueChange={(value) => setPlan(value as string)}>
  ...
</RadioGroup>

Props

RadioGroup

PropTypeDefault
value / defaultValuestringnone
onValueChange(value: string) => voidnone
orientation"vertical" | "horizontal""vertical"
namestringnone
disabled / readOnly / requiredbooleanfalse

It also takes every prop of the Base UI radio group.

Radio

PropTypeDefault
valuestringrequired
disabledbooleanfalse

RadioLabel

PropTypeDefault
titleReactNoderequired
descriptionReactNodenone

RadioCard

PropTypeDefault
valuestringrequired
titleReactNoderequired
descriptionReactNodenone
trailingReactNodenone
disabledbooleanfalse