Skip to content
VEXA
Forms

RadioGroup

Client leaf

A set of mutually exclusive options.

Live preview

Rendered from the installed VEXA source — the same files you receive.

Plan

Installation

Adds the component source to your project. Registry dependencies are pulled in automatically.

npx shadcn@latest add https://vexa.valfiguer.com/r/radio-group.json

Installs to components/vexa/radio-group.tsx

npm dependencies

  • @radix-ui/react-radio-group

Registry dependencies

  • @vexa/utils

Other VEXA items this component needs. Install them first — they are resolved automatically by the CLI.

Source

Copy this into its target path, or install it with the command above.

components/vexa/radio-group.tsx
"use client";

import type * as React from "react";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";

import { cn } from "@/lib/utils";

/**
 * RadioGroup — a set of mutually exclusive options.
 *
 * Client leaf. Radix provides the `radiogroup`/`radio` roles and the APG roving
 * focus model: Arrow keys move between and select options, Tab moves in and out
 * of the group. Give the group an accessible name with `aria-label` or
 * `aria-labelledby`, and associate each item with a `<Label htmlFor>`.
 */
export function RadioGroup({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
  return (
    <RadioGroupPrimitive.Root
      data-slot="radio-group"
      className={cn("grid gap-2.5", className)}
      {...props}
    />
  );
}

export function RadioGroupItem({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
  return (
    <RadioGroupPrimitive.Item
      data-slot="radio-group-item"
      className={cn(
        "aspect-square size-5 shrink-0 rounded-full border border-vexa-input bg-vexa-surface-raised shadow-vexa-sm",
        "transition-[color,border-color,box-shadow] duration-[var(--vexa-duration-fast)] ease-[var(--vexa-ease-standard)]",
        "outline-none focus-visible:ring-2 focus-visible:ring-vexa-ring focus-visible:ring-offset-2 focus-visible:ring-offset-vexa-background",
        "disabled:cursor-not-allowed disabled:opacity-50",
        "data-[state=checked]:border-vexa-primary",
        "aria-[invalid=true]:border-vexa-danger",
        className,
      )}
      {...props}
    >
      <RadioGroupPrimitive.Indicator
        data-slot="radio-group-indicator"
        className="flex items-center justify-center"
      >
        <span className="size-2.5 rounded-full bg-vexa-primary" />
      </RadioGroupPrimitive.Indicator>
    </RadioGroupPrimitive.Item>
  );
}

Details

Example

tsx
<RadioGroup defaultValue="a" aria-label="Plan"><div className="flex items-center gap-2"><RadioGroupItem value="a" id="a" /><Label htmlFor="a">Starter</Label></div></RadioGroup>

Accessibility

APG radiogroup model: Arrow keys move and select, Tab enters/exits the group. Give the group a name via aria-label/aria-labelledby and label each item.