Skip to content
Esc
navigateopen⌘Jpreview
On this page

Radio Group

A set of checkable buttons where only one can be checked at a time.

import * as stylex from '@stylexjs/stylex';

import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { space, fontSize } from '@/lib/constants.stylex';
import { colors, font } from '@/lib/tokens.stylex';

const options = ['Default', 'Comfortable', 'Compact'];

export default function RadioGroupDemo() {
  return (
    <RadioGroup defaultValue="Comfortable">
      {options.map((option) => (
        <label key={option} {...stylex.props(styles.label)}>
          <RadioGroupItem value={option} /> {option}
        </label>
      ))}
    </RadioGroup>
  );
}

const styles = stylex.create({
  label: {
    alignItems: 'center',
    color: colors.foreground,
    display: 'flex',
    fontFamily: font.sans,
    fontSize: fontSize.sm,
    gap: space.s2,
  },
});

Install

npx shadcn@latest add @madeui/radio-group

Usage

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

Composition

<RadioGroup>
  <RadioGroupItem />
  <RadioGroupItem />
</RadioGroup>

Description

Pair each item with Field, FieldContent, and FieldDescription to add helper text.

Up to 5 projects and community support.

Unlimited projects and priority support.

import * as stylex from '@stylexjs/stylex';

import {
  Field,
  FieldContent,
  FieldDescription,
  FieldLabel,
} from '@/components/ui/field';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { space } from '@/lib/constants.stylex';

const plans = [
  {
    value: 'starter',
    label: 'Starter',
    description: 'Up to 5 projects and community support.',
  },
  {
    value: 'pro',
    label: 'Pro',
    description: 'Unlimited projects and priority support.',
  },
];

export default function RadioGroupDescription() {
  return (
    <RadioGroup defaultValue="starter" style={styles.group}>
      {plans.map((plan) => (
        <Field key={plan.value} orientation="horizontal">
          <RadioGroupItem
            value={plan.value}
            id={`radio-group-description-${plan.value}`}
          />
          <FieldContent>
            <FieldLabel htmlFor={`radio-group-description-${plan.value}`}>
              {plan.label}
            </FieldLabel>
            <FieldDescription>{plan.description}</FieldDescription>
          </FieldContent>
        </Field>
      ))}
    </RadioGroup>
  );
}

const styles = stylex.create({
  group: {
    gap: space.s4,
  },
});

Choice card

Wrap each Field in FieldLabel for a clickable, card-style selection.

import * as stylex from '@stylexjs/stylex';

import {
  Field,
  FieldContent,
  FieldDescription,
  FieldLabel,
  FieldTitle,
} from '@/components/ui/field';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { space, stroke } from '@/lib/constants.stylex';
import { colors, radius } from '@/lib/tokens.stylex';

const plans = [
  {
    value: 'starter',
    label: 'Starter',
    description: 'Up to 5 projects and community support.',
  },
  {
    value: 'pro',
    label: 'Pro',
    description: 'Unlimited projects and priority support.',
  },
];

export default function RadioGroupChoiceCard() {
  return (
    <RadioGroup defaultValue="pro" style={styles.group}>
      {plans.map((plan) => (
        <Field key={plan.value} orientation="horizontal">
          {/* The label is the card, so the whole surface toggles the radio. */}
          <FieldLabel style={styles.card}>
            <RadioGroupItem value={plan.value} />
            <FieldContent>
              <FieldTitle>{plan.label}</FieldTitle>
              <FieldDescription>{plan.description}</FieldDescription>
            </FieldContent>
          </FieldLabel>
        </Field>
      ))}
    </RadioGroup>
  );
}

const styles = stylex.create({
  group: {
    gap: space.s3,
  },
  card: {
    alignItems: 'flex-start',
    borderColor: {
      default: colors.border,
      ':has([data-checked])': colors.primary,
    },
    borderRadius: radius.lg,
    borderStyle: 'solid',
    borderWidth: stroke.border,
    cursor: 'pointer',
    display: 'flex',
    gap: space.s3,
    padding: space.s4,
  },
});

Disabled

Set disabled on the group to disable every item, or on a single RadioGroupItem.

import * as stylex from '@stylexjs/stylex';

import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { space, fontSize } from '@/lib/constants.stylex';
import { colors, font } from '@/lib/tokens.stylex';

export default function RadioGroupDisabled() {
  return (
    <RadioGroup defaultValue="a" disabled>
      <label {...stylex.props(styles.label, styles.disabled)}>
        <RadioGroupItem value="a" /> Option A
      </label>
      <label {...stylex.props(styles.label, styles.disabled)}>
        <RadioGroupItem value="b" /> Option B
      </label>
    </RadioGroup>
  );
}

const styles = stylex.create({
  label: {
    alignItems: 'center',
    color: colors.foreground,
    display: 'flex',
    fontFamily: font.sans,
    fontSize: fontSize.sm,
    gap: space.s2,
  },
  disabled: {
    opacity: 0.5,
  },
});

API reference

Built on Base UI Radio. The tables below cover the props this library adds or changes — every other prop is forwarded to the underlying Base UI part; see the Base UI Radio API reference for the full list.

RadioGroup

Prop Type Default Description
value any Controlled selected value.
defaultValue any
onValueChange (value: any) => void
disabled boolean false
style StyleXStyles StyleX styles merged last — always win over the component’s own styles.

RadioGroupItem

Prop Type Default Description
value (required) any
disabled boolean false
style StyleXStyles StyleX styles merged last — always win over the component’s own styles.

Was this page helpful?