Radio Group

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

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

Bits UI offers several approaches to manage and synchronize the Radio Group's value state, catering to different levels of control and integration needs.

1. Two-Way Binding

For seamless state synchronization, use Svelte's bind:value directive. This method automatically keeps your local state in sync with the component's internal state.

	<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>

Key Benefits

  • Simplifies state management
  • Automatically updates myValue when the internal state changes (e.g., via clicking on an item)
  • Allows external control (e.g., selecting an item via a separate button)

2. Change Handler

For more granular control or to perform additional logic on state changes, use the onValueChange prop. This approach is useful when you need to execute custom logic alongside state updates.

	<script lang="ts">
	import { RadioGroup } from "bits-ui";
	let myValue = $state("");
</script>
 
<RadioGroup.Root
	value={myValue}
	onValueChange={(v) => {
		myValue = v;
		// additional logic here.
	}}
>
	<!-- ... -->
</RadioGroup.Root>

Use Cases

  • Implementing custom behaviors on value change
  • Integrating with external state management solutions
  • Triggering side effects (e.g., logging, data fetching)

3. Fully Controlled

For complete control over the component's value state, use the controlledValue prop. This approach requires you to manually manage the value state, giving you full control over when and how the component responds to value change events.

To implement controlled state:

  1. Set the controlledValue prop to true on the RadioGroup.Root component.
  2. Provide a value prop to RadioGroup.Root, which should be a variable holding the current state.
  3. Implement an onValueChange handler to update the state when the internal state changes.
	<script lang="ts">
	import { RadioGroup } from "bits-ui";
	let myValue = $state("");
</script>
 
<RadioGroup.Root controlledValue value={myValue} onValueChange={(v) => (myValue = v)}>
	<!-- ... -->
</RadioGroup.Root>

When to Use

  • Implementing complex open/close logic
  • Coordinating multiple UI elements
  • Debugging state-related issues

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>

API Reference

RadioGroup.Root

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

Property Type Description
value $bindable
string

The value of the currently selected radio item. You can bind to this value to control the radio group's value from outside the component.

Default: undefined
onValueChange
function

A callback that is fired when the radio group's value changes.

Default: undefined
controlledValue
boolean

Whether or not the value is controlled or not. If true, the component will not update the value state internally, instead it will call onValueChange when it would have otherwise, and it is up to you to update the value prop that is passed to the component. See Controlled State for more information.

Default: false
disabled
boolean

Whether or not the radio group is disabled. This prevents the user from interacting with it.

Default: false
required
boolean

Whether or not the radio group is required.

Default: false
name
string

The name of the radio group used in form submission. If provided, a hidden input element will be rendered to submit the value of the radio group.

Default: undefined
loop
boolean

Whether or not the radio group should loop through the items when navigating with the arrow keys.

Default: false
orientation
enum

The orientation of the radio group. This will determine how keyboard navigation will work within the component.

Default: 'vertical'
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See Child Snippet docs for more information.

Default: undefined
Data Attribute Value Description
data-orientation
enum

The orientation of the radio group.

data-radio-group-root
''

Present on the root element.

RadioGroup.Item

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

Property Type Description
value required
string

The value of the radio item. This should be unique for each radio item in the group.

Default: undefined
disabled
boolean

Whether the radio item is disabled.

Default: false
ref $bindable
HTMLButtonElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See Child Snippet docs for more information.

Default: undefined
Data Attribute Value Description
data-disabled
''

Present when the radio item is disabled.

data-value
''

The value of the radio item.

data-state
enum

The radio item's checked state.

data-orientation
enum

The orientation of the parent radio group.

data-radio-group-item
''

Present on the radio item element.