RadioGroup
Pick exactly one from a short list where every option is on show.
SourcePackage @kerf-js/veneer
Preview
tsx
import { state } from '@kerf-js/core'import { RadioGroup } from '@kerf-js/veneer'export function Window() { const when = state<'now' | 'night' | 'weekend'>('now') return ( <RadioGroup label="Maintenance window" value={when.value} options={[...WINDOWS]} onChange={(v) => (when.value = v)} /> )}const WINDOWS = [ { value: 'now', label: 'Right away', hint: 'Starts within a minute.', }, { value: 'night', label: 'Tonight', hint: "At 02:00 in the project's region.", }, { value: 'weekend', label: 'This weekend', hint: 'Saturday at 02:00.', disabled: true, },] as constInstallation
npm install @kerf-js/veneerUsage
tsx
import { RadioGroup } from '@kerf-js/veneer'Labels only
Leave out hint when the label says enough.
tsx
import { state } from '@kerf-js/core'import { RadioGroup } from '@kerf-js/veneer'export function Plain() { const branch = state('main') return ( <RadioGroup label="Deploy from" value={branch.value} options={[ { value: 'main', label: 'main' }, { value: 'release', label: 'release' }, { value: 'tags', label: 'Tagged commits only' }, ]} onChange={(v) => (branch.value = v)} /> )}More than words
A label can be any content, like a badge with each region's latency.
tsx
import { state } from '@kerf-js/core'import { Badge, RadioGroup, Row } from '@kerf-js/veneer'export function Rich() { const region = state('eu-west') return ( <RadioGroup label="Region" value={region.value} options={NEAR} onChange={(v) => (region.value = v)} /> )}const LATENCY = { 'eu-west': 18, 'us-east': 94, 'ap-south': 162,} as Record<string, number>const NEAR = Object.keys(LATENCY).map((r) => ({ value: r, label: ( <Row gap="related"> {r} <Badge tone={ LATENCY[r]! < 50 ? 'positive' : LATENCY[r]! < 120 ? 'caution' : 'critical' } > {LATENCY[r]} ms </Badge> </Row> ), hint: r === 'eu-west' ? 'Closest to you.' : undefined,}))Choose a plan
Plan and seats both change the total.
tsx
import { state } from '@kerf-js/core'import { RadioGroup, Row, Slider, Stack, Text} from '@kerf-js/veneer'export function Plan() { const plan = state('team') const seats = state(8) return ( <Stack gap="group"> <RadioGroup label="Plan" value={plan.value} options={PLANS} onChange={(v) => (plan.value = v)} /> <Slider label="Seats" value={seats.value} min={1} max={40} onChange={(v) => (seats.value = v)} /> <Row justify="between"> <Text subtle>Per month</Text> <Text role="heading"> €{SEAT[plan.value]! * seats.value} </Text> </Row> </Stack> )}const min = 60_000const PLANS = [ { value: 'hobby', label: 'Hobby', hint: 'One project, free.', }, { value: 'team', label: 'Team', hint: '€20 a seat, unlimited projects.', }, { value: 'scale', label: 'Scale', hint: '€60 a seat, plus private regions.', },]const SEAT = { hobby: 0, team: 20, scale: 60,} as Record<string, number>In a form
name is what the choice posts under. Save reads it back from the form's data.
tsx
import { state } from '@kerf-js/core'import { Button, RadioGroup, Row, Stack, Text} from '@kerf-js/veneer'export function Form() { const digest = state('daily') const sent = state('') const save = (e: Event) => { e.preventDefault() const form = new FormData( e.currentTarget as HTMLFormElement, ) const value = form.get('digest') as string | null sent.value = `digest=${value ?? ''}` } return ( <Stack as="form" gap="group" onSubmit={save} > <Text role="heading">Failure emails</Text> <RadioGroup label="Failure emails" name="digest" value={digest.value} options={DIGESTS} onChange={(v) => (digest.value = v)} /> <Row justify="between"> <Text subtle role="caption" > {sent.value ? `Posted ${sent.value}` : 'Nothing saved yet'} </Text> <Button type="submit">Save</Button> </Row> </Stack> )}const DIGESTS = [ { value: 'instant', label: 'As it happens', hint: 'One email for each failed deploy.', }, { value: 'daily', label: 'Daily digest', hint: 'At 08:00, if anything failed.', }, { value: 'off', label: 'Off', hint: 'Check the dashboard yourself.', },]When to use
Accessibility
- ↑↓
- Moves to the next choice and picks it
- Tab
- Moves in and out of the group
labelnames the group for screen readers, but isn't drawn. Put a heading above it when people need one too.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | The group's accessible name. |
value | V | — | The value that is picked. |
options | RadioOption<V>[] | — | The choices: { value, label, hint, disabled }. |
name | string | — | The form field name. Made up for you when left out. |
| Event | Type | Default | Description |
|---|---|---|---|
onChange | (v: V) => void | — | Called with the new value. |