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.
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#
| Prop | Type | Default |
|---|---|---|
value / defaultValue | string | none |
onValueChange | (value: string) => void | none |
orientation | "vertical" | "horizontal" | "vertical" |
name | string | none |
disabled / readOnly / required | boolean | false |
It also takes every prop of the Base UI radio group.
Radio#
| Prop | Type | Default |
|---|---|---|
value | string | required |
disabled | boolean | false |
RadioLabel#
| Prop | Type | Default |
|---|---|---|
title | ReactNode | required |
description | ReactNode | none |
RadioCard#
| Prop | Type | Default |
|---|---|---|
value | string | required |
title | ReactNode | required |
description | ReactNode | none |
trailing | ReactNode | none |
disabled | boolean | false |