Creative Tim UICreative Tim UI

Shadcn Radio

PreviousNext

A radio button group component that allows users to select a single option from a set of mutually exclusive choices, perfect for forms and surveys.

import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

export function RadioDemo() {
  return (
    <RadioGroup defaultValue="html">
      <div className="flex items-center gap-2">
        <RadioGroupItem value="html" id="html" />
        <Label htmlFor="html" className="text-foreground cursor-pointer">
          HTML
        </Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupItem value="react" id="react" />
        <Label htmlFor="react" className="text-foreground cursor-pointer">
          React
        </Label>
      </div>
    </RadioGroup>
  )
}

Installation

pnpm dlx @creative-tim/ui@latest add radio-group

Usage

import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<RadioGroup defaultValue="option1">
  <RadioGroupItem value="option1" id="option1" />
  <RadioGroupItem value="option2" id="option2" />
</RadioGroup>

Examples

Radio Colors

Radio components with different color schemes (primary, secondary, info, success, warning, error) to visually distinguish between different options and provide visual variety.

import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

export function RadioColors() {
  return (
    <div className="flex flex-col gap-4">
      <RadioGroup defaultValue="primary-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="primary-1"
          className="border-primary/50 data-[state=checked]:border-primary data-[state=checked]:bg-primary/10 [&_[data-slot=radio-group-indicator]_svg]:fill-primary"
        />
        <RadioGroupItem
          value="primary-2"
          className="border-primary/50 data-[state=checked]:border-primary data-[state=checked]:bg-primary/10 [&_[data-slot=radio-group-indicator]_svg]:fill-primary"
        />
      </RadioGroup>
      <RadioGroup defaultValue="secondary-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="secondary-1"
          className="border-secondary/50 data-[state=checked]:border-secondary data-[state=checked]:bg-secondary/10 [&_[data-slot=radio-group-indicator]_svg]:fill-secondary"
        />
        <RadioGroupItem
          value="secondary-2"
          className="border-secondary/50 data-[state=checked]:border-secondary data-[state=checked]:bg-secondary/10 [&_[data-slot=radio-group-indicator]_svg]:fill-secondary"
        />
      </RadioGroup>
      <RadioGroup defaultValue="info-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="info-1"
          className="border-blue-500/50 data-[state=checked]:border-blue-500 data-[state=checked]:bg-blue-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-blue-500"
        />
        <RadioGroupItem
          value="info-2"
          className="border-blue-500/50 data-[state=checked]:border-blue-500 data-[state=checked]:bg-blue-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-blue-500"
        />
      </RadioGroup>
      <RadioGroup defaultValue="success-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="success-1"
          className="border-green-500/50 data-[state=checked]:border-green-500 data-[state=checked]:bg-green-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-green-500"
        />
        <RadioGroupItem
          value="success-2"
          className="border-green-500/50 data-[state=checked]:border-green-500 data-[state=checked]:bg-green-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-green-500"
        />
      </RadioGroup>
      <RadioGroup defaultValue="warning-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="warning-1"
          className="border-yellow-500/50 data-[state=checked]:border-yellow-500 data-[state=checked]:bg-yellow-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-yellow-500"
        />
        <RadioGroupItem
          value="warning-2"
          className="border-yellow-500/50 data-[state=checked]:border-yellow-500 data-[state=checked]:bg-yellow-500/10 [&_[data-slot=radio-group-indicator]_svg]:fill-yellow-500"
        />
      </RadioGroup>
      <RadioGroup defaultValue="error-1" className="flex flex-row gap-4">
        <RadioGroupItem
          value="error-1"
          className="border-destructive/50 data-[state=checked]:border-destructive data-[state=checked]:bg-destructive/10 [&_[data-slot=radio-group-indicator]_svg]:fill-destructive"
        />
        <RadioGroupItem
          value="error-2"
          className="border-destructive/50 data-[state=checked]:border-destructive data-[state=checked]:bg-destructive/10 [&_[data-slot=radio-group-indicator]_svg]:fill-destructive"
        />
      </RadioGroup>
    </div>
  )
}

Disabled Radio

A disabled radio group that cannot be interacted with, useful for displaying read-only options or temporarily restricting user interaction.

import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

export function DisabledRadio() {
  return (
    <RadioGroup defaultValue="html">
      <div className="flex items-center gap-2">
        <RadioGroupItem value="html" id="html" disabled />
        <Label htmlFor="html" className="text-foreground cursor-pointer">
          HTML
        </Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupItem value="react" id="react" disabled />
        <Label htmlFor="react" className="text-foreground cursor-pointer">
          React
        </Label>
      </div>
    </RadioGroup>
  )
}

Radio with Description

A radio group component with labels and descriptive text that provides additional context and guidance to help users understand each option.

import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

export function RadioWithDescription() {
  return (
    <RadioGroup defaultValue="html" className="gap-4">
      <div className="flex gap-2">
        <RadioGroupItem value="html" id="radio-description-html" />
        <label
          htmlFor="radio-description-html"
          className="flex -translate-y-1 cursor-pointer flex-col items-start text-left"
        >
          <span className="font-semibold">HTML Version</span>
          <span className="text-foreground text-sm">
            Creative Tim UI HTML, packed with rich components and widgets.
          </span>
        </label>
      </div>
      <div className="flex gap-2">
        <RadioGroupItem value="react" id="radio-description-react" />
        <label
          htmlFor="radio-description-react"
          className="flex -translate-y-1 cursor-pointer flex-col items-start text-left"
        >
          <span className="font-semibold">React Version</span>
          <span className="text-foreground text-sm">
            Creative Tim UI React, packed with rich components and widgets.
          </span>
        </label>
      </div>
    </RadioGroup>
  )
}

A radio group component with labels that include clickable links, perfect for terms and conditions, privacy policies, or related documentation.

import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

export function RadioWithLink() {
  return (
    <RadioGroup defaultValue="agree">
      <div className="flex items-center gap-2">
        <RadioGroupItem value="agree" id="radio-link-agree" />
        <Label
          htmlFor="radio-link-agree"
          className="text-foreground flex cursor-pointer gap-1"
        >
          I agree with the{" "}
          <a href="#" className="text-primary underline">
            terms and conditions
          </a>
        </Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupItem value="disagree" id="radio-link-disagree" />
        <Label
          htmlFor="radio-link-disagree"
          className="text-foreground flex cursor-pointer gap-1"
        >
          I disagree with the{" "}
          <a href="#" className="text-primary underline">
            terms and conditions
          </a>
        </Label>
      </div>
    </RadioGroup>
  )
}

Radio with Custom Icon

A radio group component with custom star icons that toggle between outlined and filled states, providing a more intuitive visual indicator for selected options.

"use client"

import * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { Star } from "lucide-react"

import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
import { RadioGroup } from "@/components/ui/radio-group"

export function RadioWithCustomIcon() {
  return (
    <RadioGroup defaultValue="html">
      <div className="flex items-center gap-2">
        <RadioGroupPrimitive.Item
          value="html"
          id="custom-icon-html"
          className={cn(
            "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 group relative flex aspect-square size-5 shrink-0 items-center justify-center rounded-full border bg-transparent shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-transparent"
          )}
        >
          <Star className="text-primary pointer-events-none absolute top-1/2 left-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 opacity-100 transition-opacity group-data-[state=checked]:opacity-0" />
          <RadioGroupPrimitive.Indicator className="relative flex items-center justify-center">
            <Star className="text-primary fill-primary absolute top-1/2 left-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2" />
          </RadioGroupPrimitive.Indicator>
        </RadioGroupPrimitive.Item>
        <Label
          htmlFor="custom-icon-html"
          className="text-foreground cursor-pointer"
        >
          HTML
        </Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupPrimitive.Item
          value="react"
          id="custom-icon-react"
          className={cn(
            "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 group relative flex aspect-square size-5 shrink-0 items-center justify-center rounded-full border bg-transparent shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-transparent"
          )}
        >
          <Star className="text-primary pointer-events-none absolute top-1/2 left-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 opacity-100 transition-opacity group-data-[state=checked]:opacity-0" />
          <RadioGroupPrimitive.Indicator className="relative flex items-center justify-center">
            <Star className="text-primary fill-primary absolute top-1/2 left-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2" />
          </RadioGroupPrimitive.Indicator>
        </RadioGroupPrimitive.Item>
        <Label
          htmlFor="custom-icon-react"
          className="text-foreground cursor-pointer"
        >
          React
        </Label>
      </div>
    </RadioGroup>
  )
}

Custom Radio

A radio group component with custom checkmark icons and rounded styling that demonstrates how to personalize the appearance to match your brand or design requirements.

"use client"

import * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { Check } from "lucide-react"

import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
import { RadioGroup } from "@/components/ui/radio-group"

export function CustomRadio() {
  return (
    <RadioGroup defaultValue="html">
      <div className="flex items-center gap-2">
        <RadioGroupPrimitive.Item
          value="html"
          id="custom-html"
          className={cn(
            "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
          )}
        >
          <RadioGroupPrimitive.Indicator className="relative flex items-center justify-center">
            <Check className="h-4 w-4 stroke-2" />
          </RadioGroupPrimitive.Indicator>
        </RadioGroupPrimitive.Item>
        <Label htmlFor="custom-html" className="text-foreground cursor-pointer">
          HTML
        </Label>
      </div>
      <div className="flex items-center gap-2">
        <RadioGroupPrimitive.Item
          value="react"
          id="custom-react"
          className={cn(
            "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
          )}
        >
          <RadioGroupPrimitive.Indicator className="relative flex items-center justify-center">
            <Check className="h-4 w-4 stroke-2" />
          </RadioGroupPrimitive.Indicator>
        </RadioGroupPrimitive.Item>
        <Label
          htmlFor="custom-react"
          className="text-foreground cursor-pointer"
        >
          React
        </Label>
      </div>
    </RadioGroup>
  )
}

Props

The radio group component is built using Radix UI's RadioGroup primitive and accepts the following props:

RadioGroup (Root Component)

PropTypeDefaultDescription
valuestring-The controlled value of the selected radio item
defaultValuestring-The value of the radio item to select by default (uncontrolled)
onValueChange(value: string) => void-Event handler called when the selected value changes
disabledbooleanfalseWhen true, prevents the user from interacting with the radio group
requiredbooleanfalseWhen true, indicates that a selection is required
namestring-The name of the radio group when used in a form
orientation"horizontal" | "vertical""vertical"The orientation of the radio group
classNamestring-Additional CSS classes to apply to the radio group

RadioGroupItem

PropTypeDefaultDescription
valuestring-A unique value for the item (required)
disabledbooleanfalseWhen true, prevents the user from interacting with the item
idstring-The id of the radio item for label association
classNamestring-Additional CSS classes to apply to the item

Accessibility

The radio group component follows accessibility best practices:

  • Uses proper ARIA attributes for screen readers
  • Supports keyboard navigation (Arrow keys to navigate, Space to select)
  • Focus states are clearly visible for keyboard users
  • Disabled state is properly communicated to assistive technologies
  • Works with form labels using htmlFor and id attributes
  • Maintains proper semantic HTML structure
  • Groups related radio items together for better screen reader navigation

Best Practices

  • Use radio groups for mutually exclusive choices (only one option can be selected)
  • Provide clear labels that describe each option
  • Use descriptive text when options might not be immediately clear
  • Consider using defaultValue for common or recommended selections
  • Use disabled state to show unavailable options without hiding them
  • Ensure sufficient contrast between selected and unselected states
  • Test keyboard navigation and screen reader compatibility
  • Group related radio items together for better organization
  • Use horizontal layout when space is limited and options are short
  • Use vertical layout for longer option labels or when space allows

The radio group component provides a clean and accessible way to create single-selection controls. It's ideal for forms, surveys, preference settings, and any interface where users need to choose one option from multiple alternatives.