Notification preference
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('email');
</script>

<Radio.Group legend="Notification preference" bind:value>
  <Radio.Item label="Email" value="email" />
  <Radio.Item label="SMS" value="sms" />
  <Radio.Item label="Push notification" value="push" />
</Radio.Group>

Installation

Barrel

import { Radio, RadioGroup, RadioItem, RadioLegend } from 'kumo-svelte';

Granular

import { Radio, RadioGroup, RadioItem, RadioLegend } from 'kumo-svelte/components/radio';

Usage

<script lang="ts">import { Radio } from "kumo-svelte";</script><Radio.Group legend="Choose an option" defaultValue="a">  <Radio.Item label="Option A" value="a" />  <Radio.Item label="Option B" value="b" /></Radio.Group>

Examples

Default (Vertical)

Radio groups display vertically by default. Each radio has a label displayed to its right.

Account type
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('personal');
</script>

<Radio.Group legend="Account type" bind:value>
  <Radio.Item label="Personal" value="personal" />
  <Radio.Item label="Business" value="business" />
  <Radio.Item label="Enterprise" value="enterprise" />
</Radio.Group>

Horizontal

Use orientation="horizontal" for inline layouts. Items wrap when there isn't enough space.

Size
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('md');
</script>

<Radio.Group legend="Size" orientation="horizontal" bind:value>
  <Radio.Item label="Small" value="sm" />
  <Radio.Item label="Medium" value="md" />
  <Radio.Item label="Large" value="lg" />
</Radio.Group>

With Description

Add helper text below the radio items using the description prop.

Shipping method

Choose how you'd like to receive your order

<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('standard');
</script>

<Radio.Group
  legend="Shipping method"
  description="Choose how you'd like to receive your order"
  bind:value
>
  <Radio.Item label="Standard (5-7 days)" value="standard" />
  <Radio.Item label="Express (2-3 days)" value="express" />
  <Radio.Item label="Overnight" value="overnight" />
</Radio.Group>

Control Position

Use controlPosition="end" to place labels before radio buttons.

Preferences
<script lang="ts">
  import { Radio } from '$lib';
</script>

<Radio.Group legend="Preferences" controlPosition="end" defaultValue="a">
  <Radio.Item label="Label before radio" value="a" />
  <Radio.Item label="Another option" value="b" />
</Radio.Group>

Radio Card

Use appearance="card" on the group to display each option as a selectable card. Combine with the description prop on each item for richer content.

Choose a plan
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('free');
</script>

<Radio.Group legend="Choose a plan" appearance="card" bind:value>
  <Radio.Item
    label="Free"
    description="For personal or hobby projects that aren't business-critical."
    value="free"
  />
  <Radio.Item
    label="Pro"
    description="For professional websites that aren't business-critical."
    value="pro"
  />
  <Radio.Item
    label="Business"
    description="For small businesses operating online."
    value="business"
  />
  <Radio.Item
    label="Contract"
    description="For mission-critical applications that are core to your business."
    value="contract"
  />
</Radio.Group>

Radio Card (Control on the Left)

Use controlPosition="start" on a card radio group to place the radio control on the left of the label and description.

Choose a plan
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('free');
</script>

<Radio.Group legend="Choose a plan" appearance="card" controlPosition="start" bind:value>
  <Radio.Item
    label="Free"
    description="For personal or hobby projects that aren't business-critical."
    value="free"
  />
  <Radio.Item
    label="Pro"
    description="For professional websites that aren't business-critical."
    value="pro"
  />
  <Radio.Item
    label="Business"
    description="For small businesses operating online."
    value="business"
  />
  <Radio.Item
    label="Contract"
    description="For mission-critical applications that are core to your business."
    value="contract"
  />
</Radio.Group>

Rich Label Content

The label prop on Radio.Item accepts Svelte snippets, so you can embed icons, badges, or other markup alongside the text.

Choose a plan
<script lang="ts">
  import { Badge, Radio } from '$lib';

  let value = $state('pro');
</script>

{#snippet freeLabel()}
  <span class="flex items-center gap-2">
    Free
    <Badge variant="neutral">$0</Badge>
  </span>
{/snippet}

{#snippet proLabel()}
  <span class="flex items-center gap-2">
    Pro
    <Badge variant="primary">Popular</Badge>
  </span>
{/snippet}

<Radio.Group legend="Choose a plan" appearance="card" bind:value>
  <Radio.Item label={freeLabel} description="For personal or hobby projects." value="free" />
  <Radio.Item label={proLabel} description="For professional websites." value="pro" />
</Radio.Group>

Radio Card (Horizontal)

Combine appearance="card" with orientation="horizontal" for a side-by-side card layout.

Choose a plan
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('free');
</script>

<Radio.Group legend="Choose a plan" appearance="card" orientation="horizontal" bind:value>
  <Radio.Item
    label="Free"
    description="For personal or hobby projects that aren't business-critical."
    value="free"
  />
  <Radio.Item
    label="Pro"
    description="For professional websites that aren't business-critical."
    value="pro"
  />
  <Radio.Item
    label="Business"
    description="For small businesses operating online."
    value="business"
  />
  <Radio.Item
    label="Contract"
    description="For mission-critical applications that are core to your business."
    value="contract"
  />
</Radio.Group>

With Error

Show validation errors at the group level using the error prop.

Payment method

Please select a payment method to continue

Payment method

Please select a payment method to continue

<script lang="ts">
  import { Radio } from '$lib';
</script>

<div class="grid grid-cols-2 gap-6">
  <Radio.Group legend="Payment method" error="Please select a payment method to continue">
    <Radio.Item label="Credit Card" value="card" variant="error" />
    <Radio.Item label="PayPal" value="paypal" variant="error" />
  </Radio.Group>
  <Radio.Group
    legend="Payment method"
    appearance="card"
    error="Please select a payment method to continue"
  >
    <Radio.Item
      label="Credit Card"
      description="Pay with Visa, Mastercard, American Express, or Elo."
      value="card"
      variant="error"
    />
    <Radio.Item
      label="PayPal"
      description="Pay with your PayPal account."
      value="paypal"
      variant="error"
    />
  </Radio.Group>
</div>

Disabled

Disable the entire group or individual items.

Disabled group
Individual disabled
Disabled card group
Individual disabled card
<script lang="ts">
  import { Radio } from '$lib';
</script>

<div class="grid grid-cols-2 gap-6">
  <Radio.Group legend="Disabled group" disabled defaultValue="a">
    <Radio.Item label="Option A" value="a" />
    <Radio.Item label="Option B" value="b" />
  </Radio.Group>
  <Radio.Group legend="Individual disabled" defaultValue="available">
    <Radio.Item label="Available" value="available" />
    <Radio.Item label="Unavailable" value="unavailable" disabled />
  </Radio.Group>
  <Radio.Group legend="Disabled card group" appearance="card" disabled defaultValue="a">
    <Radio.Item label="Option A" description="This option is disabled." value="a" />
    <Radio.Item label="Option B" description="This option is disabled." value="b" />
  </Radio.Group>
  <Radio.Group legend="Individual disabled card" appearance="card" defaultValue="available">
    <Radio.Item
      label="Available"
      description="This option can be selected."
      value="available"
    />
    <Radio.Item
      label="Unavailable"
      description="This option is not available."
      value="unavailable"
      disabled
    />
  </Radio.Group>
</div>

Visually Hidden Legend

Use Radio.Legend with class="sr-only" to keep the legend accessible to screen readers while hiding it visually. This is useful when the radio group is already labeled by a parent Field or heading, and showing the legend would create a redundant label.

Paths
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('all');
</script>

<Radio.Group defaultValue="all" bind:value>
  <Radio.Legend class="sr-only">Paths</Radio.Legend>
  <Radio.Item label="Allow all paths" value="all" />
  <Radio.Item label="Restrict to specific paths" value="specific" />
</Radio.Group>

Custom Legend Styling

Radio.Legend accepts class for full control over legend presentation. Use it instead of the legend string prop when you need custom typography, colors, or layout.

Notification preference
<script lang="ts">
  import { Radio } from '$lib';

  let value = $state('email');
</script>

<Radio.Group bind:value>
  <Radio.Legend class="text-sm font-normal text-kumo-subtle">
    Notification preference
  </Radio.Legend>
  <Radio.Item label="Email" value="email" />
  <Radio.Item label="SMS" value="sms" />
  <Radio.Item label="Push notification" value="push" />
</Radio.Group>

Typed Values

Use generic values when radio options are numbers, enums, or other non-string values.

Items per page
Theme
<script lang="ts">
  import { RadioGroup, RadioItem } from '$lib';

  const ThemeType = {
    dark: 'dark',
    light: 'light',
    system: 'system'
  } as const;
  type ThemeType = (typeof ThemeType)[keyof typeof ThemeType];

  let pageSize = $state<number>(10);
  let theme = $state<ThemeType>(ThemeType.system);
</script>

<div class="grid grid-cols-2 gap-6">
  <RadioGroup legend="Items per page" bind:value={pageSize}>
    <RadioItem label="10" value={10} />
    <RadioItem label="25" value={25} />
    <RadioItem label="50" value={50} />
  </RadioGroup>
  <RadioGroup legend="Theme" bind:value={theme}>
    <RadioItem label="Light" value={ThemeType.light} />
    <RadioItem label="Dark" value={ThemeType.dark} />
    <RadioItem label="System" value={ThemeType.system} />
  </RadioGroup>
</div>

API Reference

Radio.Group

Container for radio buttons with legend, description, and error support.

PropTypeDefaultDescription
legend string-legend prop.
orientation 'vertical' | 'horizontal'"vertical"Layout orientation.
appearance 'default' | 'card'"default"Visual appearance preset.
error string-Validation error message or matcher.
description string | Snippet-Supporting description text.
value Value-Controlled value. Parameterized by the radio value type Value, which defaults to string.
disabled booleanfalseDisables the component.
controlPosition 'start' | 'end'-Control position relative to label.
name string-Form field name.
defaultValue Value-Initial uncontrolled value. Parameterized by the radio value type Value, which defaults to string.
required boolean-Marks the field as required.
onValueChange (value: Value, eventDetails?: unknown) => void-Called when the value changes. The second argument carries native event details about the interaction.

Radio.Legend

Composable legend sub-component for Radio.Group. Accepts class for full styling control (e.g. class="sr-only" to visually hide). Use instead of the legend string prop when you need custom legend styling.

PropTypeDefaultDescription
No component-specific props. Accepts standard HTML attributes.

Radio.Item

Individual radio button within Radio.Group.

PropTypeDefaultDescription
disabled booleanfalseDisables the component.
variant 'default' | 'error'"default"Visual variant.
appearance 'default' | 'card'-Visual appearance preset.
label *string | Snippet-Visible label content.
description string | Snippet-Supporting description text.
value *Value-Item value. Parameterized by the radio value type Value to match the enclosing Radio.Group value type.

Accessibility

Semantic HTML

Radio.Group uses semantic <fieldset> and <legend> elements for proper grouping and screen reader announcement.

Keyboard Navigation

Arrow Up/Down or Arrow Left/Right moves between options. Space selects the focused option. Tab moves focus to and from the radio group.

Screen Readers

Each radio is announced with its label and selection state. The group legend provides context for all options.