Primitives
Radio Group
A group of options where the user selects one value.
The new period starts with the next invoice.
import { Field, Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
export default function RadioGroupHero() {
return (
<Field label="Billing period" labelAs="span" hint="The new period starts with the next invoice.">
<RadioGroup defaultValue="monthly">
<Label className="font-normal">
<RadioGroupItem value="monthly" />
Monthly
</Label>
<Label className="font-normal">
<RadioGroupItem value="yearly" />
Yearly, with two months free
</Label>
</RadioGroup>
</Field>
)
}aria-label or aria-labelledby. A Field with labelAs="span" adds a visible label.RadioGroupItem and its text in a Label.| Choice | Control |
|---|---|
| One value among 2 to 5 options | RadioGroup |
| One value among 4 to 15 options | Select |
| One value among 2 to 6 options with a description or an icon | Radio Card Group |
| An on/off choice | Switch or Checkbox |
| A compact control to change the view or the format | Toggle Group |
Import the two parts. RadioGroup holds the value. Each RadioGroupItem is one option.
import { RadioGroup, RadioGroupItem } from 'ferry-ui'
<RadioGroup>
<RadioGroupItem />
</RadioGroup>The group puts the options in a column by default. For a row, pass className="flex gap-4".
import { Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
export default function RadioGroupRow() {
return (
<RadioGroup aria-label="Export format" defaultValue="csv" className="flex gap-4">
<Label className="font-normal">
<RadioGroupItem value="csv" />
CSV
</Label>
<Label className="font-normal">
<RadioGroupItem value="pdf" />
PDF
</Label>
<Label className="font-normal">
<RadioGroupItem value="json" />
JSON
</Label>
</RadioGroup>
)
}To add a description to an option, link the Label with htmlFor. Link the description with aria-describedby.
Sees the projects and the reports.
Creates and edits the projects of the workspace.
Has full access, with billing and members.
import { Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
const ROLES = [
{ value: 'viewer', label: 'Viewer', description: 'Sees the projects and the reports.' },
{ value: 'member', label: 'Member', description: 'Creates and edits the projects of the workspace.' },
{ value: 'admin', label: 'Admin', description: 'Has full access, with billing and members.' },
]
export default function RadioGroupDescriptions() {
return (
<RadioGroup aria-label="Role" defaultValue="member" className="max-w-sm gap-4">
{ROLES.map((role) => (
<div key={role.value} className="flex items-start gap-2.5">
<RadioGroupItem
value={role.value}
id={`role-${role.value}`}
aria-describedby={`role-${role.value}-hint`}
className="mt-0.5"
/>
<div className="flex flex-col gap-0.5">
<Label htmlFor={`role-${role.value}`}>{role.label}</Label>
<p id={`role-${role.value}-hint`} className="text-[13px] text-foreground-light">
{role.description}
</p>
</div>
</div>
))}
</RadioGroup>
)
}disabled on a RadioGroupItem disables one option. disabled on RadioGroup disables all the options.
import { Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
export default function RadioGroupDisabled() {
return (
<RadioGroup aria-label="Billing period" defaultValue="monthly">
<Label className="font-normal">
<RadioGroupItem value="monthly" />
Monthly
</Label>
<Label className="font-normal">
<RadioGroupItem value="yearly" />
Yearly
</Label>
<Label className="font-normal">
<RadioGroupItem value="custom" disabled />
Custom contract, on the Enterprise plan only
</Label>
</RadioGroup>
)
}A radio group holds its value by default. Use defaultValue for the first value.
To control the value, pass value and onValueChange.
Price: $29 each month
import * as React from 'react'
import { Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
const PLANS = [
{ value: 'starter', label: 'Starter', price: '$0' },
{ value: 'pro', label: 'Pro', price: '$29' },
{ value: 'team', label: 'Team', price: '$99' },
]
export default function RadioGroupControlled() {
const [plan, setPlan] = React.useState('pro')
const price = PLANS.find((item) => item.value === plan)?.price
return (
<div className="flex flex-col gap-4">
<RadioGroup aria-label="Plan" value={plan} onValueChange={setPlan}>
{PLANS.map((item) => (
<Label key={item.value} className="font-normal">
<RadioGroupItem value={item.value} />
{item.label}
</Label>
))}
</RadioGroup>
<p className="text-[13px] text-foreground-light">Price: {price} each month</p>
</div>
)
}aria-invalid on each RadioGroupItem shows the error border. The error prop of Field shows the message as text.
import * as React from 'react'
import { Button, Field, Label, RadioGroup, RadioGroupItem, toast } from 'ferry-ui'
const FORMATS = ['CSV', 'PDF', 'JSON']
export default function RadioGroupInvalid() {
const [format, setFormat] = React.useState('')
const [error, setError] = React.useState<string>()
function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setError(format === '' ? 'Select an export format.' : undefined)
if (format !== '') toast.success('Export started')
}
function change(value: string) {
setFormat(value)
setError(undefined)
}
return (
<form noValidate onSubmit={submit} className="flex flex-col items-start gap-4">
<Field label="Export the invoices as" labelAs="span" error={error}>
<RadioGroup value={format} onValueChange={change} className="flex gap-4">
{FORMATS.map((name) => (
<Label key={name} className="font-normal">
<RadioGroupItem value={name.toLowerCase()} aria-invalid={error ? true : undefined} />
{name}
</Label>
))}
</RadioGroup>
</Field>
<Button type="submit">Export</Button>
</form>
)
}In a <form>, name adds the value of the group to the data of the form. required makes a value mandatory.
import * as React from 'react'
import { Button, Field, Label, RadioGroup, RadioGroupItem } from 'ferry-ui'
export default function RadioGroupForm() {
const [sent, setSent] = React.useState<string>()
function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
// The form holds one "visibility" entry: the value of the selected option.
setSent(String(new FormData(event.currentTarget).get('visibility')))
}
return (
<form onSubmit={submit} className="flex flex-col items-start gap-4">
<Field label="Visibility of the project" labelAs="span">
<RadioGroup name="visibility" defaultValue="private" required>
<Label className="font-normal">
<RadioGroupItem value="private" />
Private
</Label>
<Label className="font-normal">
<RadioGroupItem value="public" />
Public
</Label>
</RadioGroup>
</Field>
<Button type="submit">Save</Button>
<p role="status" className="text-[13px] text-foreground-light">
{sent !== undefined && `The form sent: ${sent}`}
</p>
</form>
)
}Each part also accepts the props of its Radix UI primitive and the attributes of its element.
| Prop | Type | Default |
|---|---|---|
name | string | - |
| See the element or the Radix UI primitive this part renders. | ||
form | string | - |
| See the element or the Radix UI primitive this part renders. | ||
required | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
disabled | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
dir | "ltr" | "rtl" | - |
| See the element or the Radix UI primitive this part renders. | ||
orientation | "horizontal" | "vertical" | - |
| See the element or the Radix UI primitive this part renders. | ||
loop | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
defaultValue | string | - |
| See the element or the Radix UI primitive this part renders. | ||
value | string | null | - |
| See the element or the Radix UI primitive this part renders. | ||
onValueChange | ((value: string) => void) | - |
| See the element or the Radix UI primitive this part renders. | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
| Prop | Type | Default |
|---|---|---|
valueRequired | string | - |
| See the element or the Radix UI primitive this part renders. | ||
asChild | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
checked | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||
required | boolean | - |
| See the element or the Radix UI primitive this part renders. | ||