Klarhimmel UI

Radio group

Stable

One choice from a few options that are all visible. Built on native radio inputs.

npx shadcn add @klarhimmel/radio-group

When 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

Breakfast

Delivered between 07:30 and 09:30

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

    Room type

    Choose a room to continue

    Payment

    Only for company bookings

    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

    Bed type

    Not sent yet.

    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

PropTypeDefaultDescription
legendReactNode-Required. The question; it names the group.
namestringautoForm field name. Radios with the same name are one group.
value / defaultValue / onValueChangestring / (value: string) => void-Controlled or uncontrolled state.
hint / invalid / disabledReactNode / boolean / boolean-Helper or error text, error styling, switch the group off.
Radio value / label / hintstring / ReactNode / ReactNode-Value and label are required; the hint sits under the label.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
Tab / Shift+TabEnters and leaves the group as one stop, landing on the chosen option
Down / RightMoves to the next option and selects it, wrapping
Up / LeftMoves to the previous option and selects it, wrapping
SpaceSelects the focused option

Native radio inputs: the browser provides the keys and the form value, also without script.

Accessibility