Radio Group

Allows users to select a single option from a list of mutually exclusive choices.

llms.txt
	<script lang="ts">
  import { Label, RadioGroup } from "bits-ui";
</script>
 
<RadioGroup.Root class="flex flex-col gap-4 text-sm font-medium">
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="amazing"
      value="amazing"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="amazing" class="pl-3">Amazing</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="average"
      value="average"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="average" class="pl-3">Average</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="terrible"
      value="terrible"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="terrible" class="pl-3">Terrible</Label.Root>
  </div>
</RadioGroup.Root>

Structure

	<script lang="ts">
  import { RadioGroup } from "bits-ui";
</script>
 
<RadioGroup.Root>
  <RadioGroup.Item>
    {#snippet children({ checked })}
      {#if checked}

      {/if}
    {/snippet}
  </RadioGroup.Item>
</RadioGroup.Root>

Reusable Components

It's recommended to use the RadioGroup primitives to create your own custom components that can be used throughout your application.

In the example below, we're creating a custom MyRadioGroup component that takes in an array of items and renders a radio group with those items along with a Label component for each item.

MyRadioGroup.svelte
	<script lang="ts">
  import {
    RadioGroup,
    Label,
    type WithoutChildrenOrChild,
    useId,
  } from "bits-ui";
 
  type Item = {
    value: string;
    label: string;
    disabled?: boolean;
  };
 
  type Props = WithoutChildrenOrChild<RadioGroup.RootProps> & {
    items: Item[];
  };
 
  let {
    value = $bindable(""),
    ref = $bindable(null),
    items,
    ...restProps
  }: Props = $props();
</script>
 
<RadioGroup.Root bind:value bind:ref {...restProps}>
  {#each items as item}
    {@const id = useId()}
    <div>
      <RadioGroup.Item {id} value={item.value} disabled={item.disabled}>
        {#snippet children({ checked })}
          {#if checked}

          {/if}
        {/snippet}
      </RadioGroup.Item>
      <Label.Root for={id}>{item.label}</Label.Root>
    </div>
  {/each}
</RadioGroup.Root>

You can then use the MyRadioGroup component in your application like so:

+page.svelte
	<script lang="ts">
  import MyRadioGroup from "$lib/components/MyRadioGroup.svelte";
 
  const myItems = [
    { value: "apple", label: "Apple" },
    { value: "banana", label: "Banana" },
    { value: "coconut", label: "Coconut", disabled: true },
  ];
</script>
 
<MyRadioGroup items={myItems} name="favoriteFruit" />

Managing Value State

This section covers how to manage the value state of the component.

Two-Way Binding

Use bind:value for simple, automatic state synchronization:

	<script lang="ts">
  import { RadioGroup } from "bits-ui";
  let myValue = $state("");
</script>
 
<button onclick={() => (myValue = "A")}> Select A </button>
 
<RadioGroup.Root bind:value={myValue}>
  <!-- ... -->
</RadioGroup.Root>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { RadioGroup } from "bits-ui";
  let myValue = $state("");
 
  function getValue() {
    return myValue;
  }
 
  function setValue(newValue: string) {
    myValue = newValue;
  }
</script>
 
<RadioGroup.Root bind:value={getValue, setValue}>
  <!-- ... -->
</RadioGroup.Root>

HTML Forms

If you set the name prop on the RadioGroup.Root component, a hidden input element will be rendered to submit the value of the radio group to a form.

	<RadioGroup.Root name="favoriteFruit">
  <!-- ... -->
</RadioGroup.Root>

Required

To make the hidden input element required you can set the required prop on the RadioGroup.Root component.

	<RadioGroup.Root required>
  <!-- ... -->
</RadioGroup.Root>

Disabling Items

You can disable a radio group item by setting the disabled prop to true.

	<RadioGroup.Item value="apple" disabled>Apple</RadioGroup.Item>

Orientation

The orientation prop is used to determine the orientation of the radio group, which influences how keyboard navigation will work.

When the orientation is set to 'vertical', the radio group will navigate through the items using the ArrowUp and ArrowDown keys. When the orientation is set to 'horizontal', the radio group will navigate through the items using the ArrowLeft and ArrowRight keys.

	<RadioGroup.Root orientation="vertical">
  <!-- ... -->
</RadioGroup.Root>
 
<RadioGroup.Root orientation="horizontal">
  <!-- ... -->
</RadioGroup.Root>

Examples

Readonly

When a radio group is readonly, users can focus and navigate through the items but cannot change the selection. This is useful for displaying information that should be visible but not editable.

	<script lang="ts">
  import { Label, RadioGroup } from "bits-ui";
</script>
 
<RadioGroup.Root
  value="average"
  readonly
  class="flex flex-col gap-4 text-sm font-medium opacity-75"
>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="readonly-amazing"
      value="amazing"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="readonly-amazing" class="pl-3">Amazing</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="readonly-average"
      value="average"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="readonly-average" class="pl-3">Average</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="readonly-terrible"
      value="terrible"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="readonly-terrible" class="pl-3">Terrible</Label.Root>
  </div>
</RadioGroup.Root>
	<RadioGroup.Root readonly>
  <!-- ... -->
</RadioGroup.Root>

Disabled

When a radio group is disabled, users cannot interact with it at all. The entire group becomes non-focusable and non-interactive.

	<script lang="ts">
  import { Label, RadioGroup } from "bits-ui";
</script>
 
<RadioGroup.Root
  value="average"
  disabled
  class="flex flex-col gap-4 text-sm font-medium opacity-50"
>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="disabled-amazing"
      value="amazing"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="disabled-amazing" class="pl-3">Amazing</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="disabled-average"
      value="average"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="disabled-average" class="pl-3">Average</Label.Root>
  </div>
  <div
    class="text-foreground group flex select-none items-center transition-all"
  >
    <RadioGroup.Item
      id="disabled-terrible"
      value="terrible"
      class="border-border-input bg-background hover:border-dark-40 data-[state=checked]:border-foreground data-[state=checked]:border-6 size-5 shrink-0 cursor-default rounded-full border transition-all duration-100 ease-in-out"
    />
    <Label.Root for="disabled-terrible" class="pl-3">Terrible</Label.Root>
  </div>
</RadioGroup.Root>
	<RadioGroup.Root disabled>
  <!-- ... -->
</RadioGroup.Root>

API Reference

RadioGroup.Root

The radio group component used to group radio items under a common name for form submission.

Property Details
value
onValueChange
disabled
required
name
loop
orientation
readonly
ref
children
child
Data Attribute Details
data-orientation
data-disabled
data-readonly
data-radio-group-root

RadioGroup.Item

An radio item, which must be a child of the RadioGroup.Root component.

Property Details
value
disabled
ref
children
child
Data Attribute Details
data-disabled
data-readonly
data-value
data-state
data-orientation
data-radio-group-item