

<Preview name="BasicRadioGroupExample" />

## Overview [#overview]

The **RadioGroup** component allows users to select a single option from a set of mutually exclusive choices. Built on Base UI primitives, it provides a fully accessible radio button group with customizable sizes and flexible styling options.

## Usage [#usage]

```tsx
import { RadioGroup, RadioGroupItem, Label } from "@tilt-legal/cubitt-components/primitives";
```

```tsx
<RadioGroup defaultValue="option-1">
  <div className="flex items-center gap-2">
    <RadioGroupItem value="option-1" id="option-1" />
    <Label htmlFor="option-1">Option 1</Label>
  </div>
  <div className="flex items-center gap-2">
    <RadioGroupItem value="option-2" id="option-2" />
    <Label htmlFor="option-2">Option 2</Label>
  </div>
</RadioGroup>
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Using Cubitt's URL-state hooks, you can sync the radio group selection with the URL by providing a `paramName`:

    ```tsx
    // Selected value synced with ?shipping=express
    <RadioGroup paramName="shipping" defaultValue="standard">
      <div className="flex items-center gap-2">
        <RadioGroupItem value="standard" id="standard" />
        <Label htmlFor="standard">Standard Shipping</Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupItem value="express" id="express" />
        <Label htmlFor="express">Express Shipping</Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupItem value="overnight" id="overnight" />
        <Label htmlFor="overnight">Overnight Shipping</Label>
      </div>
    </RadioGroup>

    // Advanced options:
    <RadioGroup
      paramName="preference"
      defaultValue="option-1"
      paramClearOnDefault={true}    // Remove param when value equals default
      paramDebounce={150}           // Debounce URL updates
      onUrlValueChange={(value) => console.log('Selected:', value)}
    >
      {/* ... */}
    </RadioGroup>
    ```
  </Accordion>
</Accordions>

***

## Examples [#examples]

### Sizes [#sizes]

Radio groups in different sizes.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="RadioGroupSizesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { RadioGroup, RadioGroupItem, Label } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <div className="flex flex-col gap-6 items-center">
          <RadioGroup defaultValue="sm-1" size="sm" className="flex flex-row gap-4">
            <Label className="text-sm" orientation="horizontal">
              <RadioGroupItem value="sm-1" id="sm-1" />
              Option 1
            </Label>
            <Label className="text-sm" orientation="horizontal">
              <RadioGroupItem value="sm-2" id="sm-2" />
              Option 2
            </Label>
            <Label className="text-sm" orientation="horizontal">
              <RadioGroupItem value="sm-3" id="sm-3" />
              Option 3
            </Label>
          </RadioGroup>
          <RadioGroup defaultValue="md-1" size="md" className="flex flex-row gap-6">
            <Label className="text-md" orientation="horizontal">
              <RadioGroupItem value="md-1" id="md-1" />
              Option 1
            </Label>
            <Label className="text-md" orientation="horizontal">
              <RadioGroupItem value="md-2" id="md-2" />
              Option 2
            </Label>
            <Label className="text-md" orientation="horizontal">
              <RadioGroupItem value="md-3" id="md-3" />
              Option 3
            </Label>
          </RadioGroup>
          <RadioGroup defaultValue="lg-1" size="lg" className="flex flex-row gap-6">
            <Label className="text-lg" orientation="horizontal">
              <RadioGroupItem value="lg-1" id="lg-1" />
              Option 1
            </Label>
            <Label className="text-lg" orientation="horizontal">
              <RadioGroupItem value="lg-2" id="lg-2" />
              Option 2
            </Label>
            <Label className="text-lg" orientation="horizontal">
              <RadioGroupItem value="lg-3" id="lg-3" />
              Option 3
            </Label>
          </RadioGroup>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Disabled [#disabled]

Radio buttons can be individually disabled.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="DisabledRadioGroupExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { RadioGroup, RadioGroupItem, Label } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <RadioGroup defaultValue="disabled-1">
          <div className="flex items-center gap-2">
            <RadioGroupItem value="disabled-1" id="disabled-1" />
            <Label htmlFor="disabled-1">Enabled option</Label>
          </div>
          <div className="flex items-center gap-2">
            <RadioGroupItem value="disabled-2" id="disabled-2" disabled />
            <Label htmlFor="disabled-2">Disabled (unselected)</Label>
          </div>
          <div className="flex items-center gap-2">
            <RadioGroupItem value="disabled-3" id="disabled-3" disabled />
            <Label htmlFor="disabled-3">Disabled option</Label>
          </div>
        </RadioGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### With Description [#with-description]

Radio buttons with additional descriptive text.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="RadioGroupWithDescriptionExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { RadioGroup, RadioGroupItem, Label } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <RadioGroup defaultValue="plan-1" className="gap-4">
          <div className="flex items-start gap-3">
            <RadioGroupItem value="plan-1" id="plan-1" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <Label htmlFor="plan-1">Free Plan</Label>
              <p className="text-sm text-muted-foreground">
                Perfect for personal projects and small teams.
              </p>
            </div>
          </div>
          <div className="flex items-start gap-3">
            <RadioGroupItem value="plan-2" id="plan-2" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <Label htmlFor="plan-2">Pro Plan</Label>
              <p className="text-sm text-muted-foreground">
                Advanced features for growing businesses.
              </p>
            </div>
          </div>
          <div className="flex items-start gap-3">
            <RadioGroupItem value="plan-3" id="plan-3" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <Label htmlFor="plan-3">Enterprise Plan</Label>
              <p className="text-sm text-muted-foreground">
                Custom solutions for large organizations.
              </p>
            </div>
          </div>
        </RadioGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### Card Style [#card-style]

Radio buttons styled as interactive cards.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="RadioGroupCardStyleExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { RadioGroup, RadioGroupItem } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <RadioGroup defaultValue="card-1" className="gap-3">
          <label
            htmlFor="card-1"
            className="flex items-start gap-3 rounded-xl border p-4 hover:bg-accent/50 transition-colors has-[:checked]:border-primary has-[:checked]:bg-primary/10 dark:has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10 cursor-pointer"
          >
            <RadioGroupItem value="card-1" id="card-1" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <span className="font-medium">Standard Delivery</span>
              <p className="text-sm text-muted-foreground">5-7 business days</p>
            </div>
          </label>
          <label
            htmlFor="card-2"
            className="flex items-start gap-3 rounded-xl border p-4 hover:bg-accent/50 transition-colors has-[:checked]:border-primary has-[:checked]:bg-primary/10 dark:has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10 cursor-pointer"
          >
            <RadioGroupItem value="card-2" id="card-2" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <span className="font-medium">Express Delivery</span>
              <p className="text-sm text-muted-foreground">2-3 business days</p>
            </div>
          </label>
          <label
            htmlFor="card-3"
            className="flex items-start gap-3 rounded-xl border p-4 hover:bg-accent/50 transition-colors has-[:checked]:border-primary has-[:checked]:bg-primary/10 dark:has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10 cursor-pointer"
          >
            <RadioGroupItem value="card-3" id="card-3" className="mt-0.5" />
            <div className="flex flex-col gap-1">
              <span className="font-medium">Overnight Delivery</span>
              <p className="text-sm text-muted-foreground">Next business day</p>
            </div>
          </label>
        </RadioGroup>
      );
    }
    ```
  </Tab>
</Tabs>

### URL State [#url-state]

Sync radio group selection with the URL. Try selecting an option and refreshing the page.

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="RadioGroupURLStateExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { RadioGroup, RadioGroupItem, Label } from "@tilt-legal/cubitt-components/primitives";

    export default function Example() {
      return (
        <RadioGroup
          paramName="demo-shipping"
          defaultValue="standard"
          paramClearOnDefault={true}
        >
          <div className="flex items-center gap-2">
            <RadioGroupItem value="standard" id="url-standard" />
            <Label htmlFor="url-standard">Standard Shipping</Label>
          </div>
          <div className="flex items-center gap-2">
            <RadioGroupItem value="express" id="url-express" />
            <Label htmlFor="url-express">Express Shipping</Label>
          </div>
          <div className="flex items-center gap-2">
            <RadioGroupItem value="overnight" id="url-overnight" />
            <Label htmlFor="url-overnight">Overnight Shipping</Label>
          </div>
        </RadioGroup>
      );
    }
    ```
  </Tab>
</Tabs>

***

## API Reference [#api-reference]

### RadioGroup [#radiogroup]

The root container component that manages the radio button group.

| Prop            | Type                      | Default | Description                                                  |
| --------------- | ------------------------- | ------- | ------------------------------------------------------------ |
| `value`         | `string`                  | -       | The controlled value of the selected radio button.           |
| `defaultValue`  | `string`                  | -       | The default value when uncontrolled.                         |
| `onValueChange` | `(value: string) => void` | -       | Callback fired when the selected value changes.              |
| `disabled`      | `boolean`                 | `false` | Whether all radio buttons in the group are disabled.         |
| `name`          | `string`                  | -       | The name attribute for the radio group (used in forms).      |
| `required`      | `boolean`                 | `false` | Whether the radio group is required in a form.               |
| `size`          | `"sm" \| "md" \| "lg"`    | `"md"`  | Size variant applied to all child RadioGroupItem components. |
| `className`     | `string`                  | -       | Additional CSS classes for the radio group container.        |
| `data-slot`     | `string`                  | -       | Data attribute for styling and testing hooks.                |

#### URL State Props [#url-state-props]

When provided with a `paramName`, the radio group will sync its selection with URL parameters via TanStack Router search params.

| Prop                  | Type                      | Default | Description                                                                  |
| --------------------- | ------------------------- | ------- | ---------------------------------------------------------------------------- |
| `paramName`           | `string`                  | —       | URL parameter name for syncing state with URL. Enables URL state management. |
| `paramValue`          | `string`                  | —       | Controlled value for the URL parameter.                                      |
| `onUrlValueChange`    | `(value: string) => void` | —       | Callback when URL parameter value changes.                                   |
| `paramClearOnDefault` | `boolean`                 | `true`  | Remove URL param when value equals default.                                  |
| `paramThrottle`       | `number`                  | —       | Throttle URL updates in milliseconds.                                        |
| `paramDebounce`       | `number`                  | —       | Debounce URL updates in milliseconds.                                        |

### RadioGroupItem [#radiogroupitem]

Individual radio button within a RadioGroup.

| Prop        | Type                   | Default | Description                                                      |
| ----------- | ---------------------- | ------- | ---------------------------------------------------------------- |
| `value`     | `string`               | -       | The unique value for this radio button (required).               |
| `id`        | `string`               | -       | The id attribute for the radio button (used with Label htmlFor). |
| `disabled`  | `boolean`              | `false` | Whether this specific radio button is disabled.                  |
| `size`      | `"sm" \| "md" \| "lg"` | -       | Size variant (inherits from RadioGroup if not specified).        |
| `className` | `string`               | -       | Additional CSS classes for the radio button.                     |
| `data-slot` | `string`               |        | Data attribute for styling and testing hooks.                    |

### Notes [#notes]

* Radio buttons must be used within a `RadioGroup` component
* Each `RadioGroupItem` should have a unique `value` prop within its group
* Use the `id` prop on `RadioGroupItem` and `htmlFor` on `Label` for proper label association
* The `size` prop on `RadioGroup` automatically applies to all child items via Context
* Individual `RadioGroupItem` components can override the group's size if needed
