# RadioGroup

> A set of mutually exclusive options, built on real input type=radio elements sharing one name so the browser gives the roving tab stop and keyboard navigation for free.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Forms
- Tags: radio, form, selection, compound, validation
- Docs: https://fancy-ui.rama.app/docs/components/radio-group
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/radio-group

## Import

```svelte
<script lang="ts">
  import { RadioGroup } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { RadioGroup } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | `""` | The selected value, bindable — "" means nothing is selected. |
| `onValueChange` | `(value: string) => void` | — | Called with the new value whenever the selection changes. |
| `name` | `string` | — | Shared name for the native radios. Generated per instance when omitted, so two groups on the same page never fight over each other's selection. |
| `disabled` | `boolean` | `false` | Disables every item in the group. |
| `required` | `boolean` | `false` | Marks the group required for native form validation. |
| `invalid` | `boolean` | `false` | Marks the group invalid — sets aria-invalid. |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | The list's stacking axis. |
| `label` | `string` | — | Accessible name, standalone. Inside a FormField that rendered its own label, its labelId wins instead; a FormField with no label of its own falls back to this. |
| `class` | `string` | — | Additional CSS classes, merged onto the root. |
| `ref` | `HTMLDivElement \| null` | `null` | Bindable reference to the root element. |
| `RadioGroupItem.value` (required) | `string` | — | This item's value — what the group's value becomes when it is picked. |
| `RadioGroupItem.disabled` | `boolean` | `false` | Disables just this item, independent of the group's own disabled. |
| `RadioGroupItem.label` | `string` | — | Visible label text. Also the fallback content, and the fallback name. |
| `RadioGroupItem.class` | `string` | — | Additional CSS classes, merged onto the wrapping label. |
| `RadioGroupItem.ref` | `HTMLInputElement \| null` | `null` | Bindable reference to the item's native input. |
| `sound` | `boolean` | `false` | Plays a select cue when the selection actually changes, once the user has enabled sound |

## Snippets

| Snippet | Description |
| --- | --- |
| `children` | The RadioGroup's content — the RadioGroupItems. |
| `RadioGroupItem.children` | Custom content rendered in place of the item's label. |

## Examples

### Basic Usage

Three options, one selected by default.

```svelte
<script lang="ts">
	import { RadioGroup, RadioGroupItem } from "fancy-ui-svelte";

	let plan = $state("a");
</script>

<RadioGroup sound bind:value={plan} label="Plan">
	<RadioGroupItem value="a" label="Option A" />
	<RadioGroupItem value="b" label="Option B" />
	<RadioGroupItem value="c" label="Option C" />
</RadioGroup>
```

### Horizontal

The list laid out along a row instead of a column.

```svelte
<script lang="ts">
	import { RadioGroup, RadioGroupItem } from "fancy-ui-svelte";

	let size = $state("md");
</script>

<RadioGroup sound bind:value={size} orientation="horizontal" label="Size">
	<RadioGroupItem value="sm" label="Small" />
	<RadioGroupItem value="md" label="Medium" />
	<RadioGroupItem value="lg" label="Large" />
</RadioGroup>
```

### Disabled Items

One option unavailable — skipped by the keyboard and never selectable.

```svelte
<script lang="ts">
	import { RadioGroup, RadioGroupItem } from "fancy-ui-svelte";

	let plan = $state("starter");
</script>

<div class="flex flex-col items-start gap-2">
	<RadioGroup sound bind:value={plan} label="Plan">
		<RadioGroupItem value="starter" label="Starter" />
		<RadioGroupItem value="team" label="Team (unavailable)" disabled />
		<RadioGroupItem value="enterprise" label="Enterprise" />
	</RadioGroup>
	<p class="text-muted-foreground text-sm">
		"Team" is unavailable on this account — it can never end up selected, and the browser's own tab
		order skips it entirely.
	</p>
</div>
```

### With FormField

Wrapped in FormField — required and the error message driven by context.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
	import { RadioGroup, RadioGroupItem } from "fancy-ui-svelte";

	let method = $state("");
</script>

<FormField
	label="Shipping method"
	required
	error={method === "" ? "Choose a shipping method to continue." : undefined}
>
	<RadioGroup sound bind:value={method}>
		<RadioGroupItem value="standard" label="Standard — 5 to 7 days" />
		<RadioGroupItem value="express" label="Express — 2 days" />
		<RadioGroupItem value="overnight" label="Overnight" />
	</RadioGroup>
</FormField>
```
