- Components
Radio group
One choice from a few options that are all visible. Built on native radio inputs.
npx shadcn add @klarhimmel/radio-groupWhen to use
Use for
Two to five mutually exclusive options that are better seen together.
Not for
Many options. Use Select. Independent yes/no choices. Use Checkbox.
Pairs with
Field, Button, forms.
Preview
Chosen: buffet
radio-group.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { RadioGroup, Radio } from '@/components/klarhimmel/ui/radio-group';
export default function Example() {
return (
<Theme theme="brand">
<RadioGroup legend="Breakfast" name="meal" defaultValue="buffet">
<Radio value="buffet" label="Buffet" />
<Radio value="tray" label="Tray to the room" />
</RadioGroup>
</Theme>
);
}Install
Import
import { RadioGroup, Radio } from '@/components/klarhimmel/ui/radio-group';- Lands in
- components/klarhimmel/ui/radio-group.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
- Render in
- Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
Examples
States
import { Radio, RadioGroup } from '@/components/klarhimmel/ui/radio-group'; export default function RadioGroupStates() { return ( <div className="grid w-[min(40rem,calc(100cqw-3rem))] gap-8 @md:grid-cols-2"> <RadioGroup legend="Room type" invalid hint="Choose a room to continue"> <Radio value="single" label="Single" /> <Radio value="double" label="Double" /> </RadioGroup> <RadioGroup legend="Payment" defaultValue="card"> <Radio value="card" label="Card" /> <Radio value="invoice" label="Invoice" disabled hint="Only for company bookings" /> <Radio value="cash" label="Pay at the desk" /> </RadioGroup> </div> ); }Form
import { useState } from 'react'; import { Button } from '@/components/klarhimmel/ui/button'; import { Radio, RadioGroup } from '@/components/klarhimmel/ui/radio-group'; export default function RadioGroupForm() { const [sent, setSent] = useState(''); return ( <form className="flex w-[min(24rem,calc(100cqw-3rem))] flex-col items-start gap-4" onSubmit={(e) => { e.preventDefault(); setSent(String(new FormData(e.currentTarget).get('bed') ?? 'nothing')); }} > <RadioGroup legend="Bed type" name="bed" defaultValue="double"> <Radio value="double" label="Double bed" /> <Radio value="twin" label="Two single beds" /> </RadioGroup> <Button type="submit">Save choice</Button> <p role="status" className="text-kh-small text-kh-muted">{sent ? `The form sent bed=${sent}.` : 'Not sent yet.'}</p> </form> ); }
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
legend | ReactNode | - | Required. The question; it names the group. |
name | string | auto | Form field name. Radios with the same name are one group. |
value / defaultValue / onValueChange | string / (value: string) => void | - | Controlled or uncontrolled state. |
hint / invalid / disabled | ReactNode / boolean / boolean | - | Helper or error text, error styling, switch the group off. |
Radio value / label / hint | string / ReactNode / ReactNode | - | Value and label are required; the hint sits under the label. |
className | string | - | Extra classes, merged after the component's own. |
Accessibility
| Key | Does |
|---|---|
Tab / Shift+Tab | Enters and leaves the group as one stop, landing on the chosen option |
Down / Right | Moves to the next option and selects it, wrapping |
Up / Left | Moves to the previous option and selects it, wrapping |
Space | Selects the focused option |
Native radio inputs: the browser provides the keys and the form value, also without script.
Accessibility