Skip to contents

Radio Group

Provides shared state to a series of radio buttons.

Best apple
import { createUniqueId } from 'solid-js';
import { Radio } from 'base-ui-solid/radio';
import { RadioGroup } from 'base-ui-solid/radio-group';
import styles from './index.module.css';

export default function ExampleRadioGroup() {
  const id = createUniqueId();
  return (
    <RadioGroup aria-labelledby={id} defaultValue="fuji-apple" class={styles.RadioGroup}>
      <div class={styles.Caption} id={id}>
        Best apple
      </div>

      <label class={styles.Item}>
        <Radio.Root value="fuji-apple" class={styles.Radio}>
          <Radio.Indicator class={styles.Indicator} />
        </Radio.Root>
        Fuji
      </label>

      <label class={styles.Item}>
        <Radio.Root value="gala-apple" class={styles.Radio}>
          <Radio.Indicator class={styles.Indicator} />
        </Radio.Root>
        Gala
      </label>

      <label class={styles.Item}>
        <Radio.Root value="granny-smith-apple" class={styles.Radio}>
          <Radio.Indicator class={styles.Indicator} />
        </Radio.Root>
        Granny Smith
      </label>
    </RadioGroup>
  );
}

Usage guidelines

  • Form controls must have an accessible name: It can be created using <label> elements, or the Field and Fieldset components. See Labeling a radio group and the forms guide.

Anatomy

Import the component and assemble its parts:

Anatomy
import { Radio } from 'base-ui-solid/radio';
import { RadioGroup } from 'base-ui-solid/radio-group';

<RadioGroup>
  <Radio.Root>
    <Radio.Indicator />
  </Radio.Root>
</RadioGroup>;

Examples

Labeling a radio group

Label the group with aria-labelledby and a sibling label element:

Using aria-labelledby to label a radio group
<div id="storage-type-label">Storage type</div>
<RadioGroup aria-labelledby="storage-type-label">{/* ... */}</RadioGroup>

An enclosing <label> is the simplest labeling pattern for each radio:

Using an enclosing label to label a radio button
<label>
  <Radio.Root value="ssd" />
  SSD
</label>

Rendering as a native button

By default, <Radio.Root> renders a <span> element to support enclosing labels. Prefer rendering each radio as a native button when using sibling labels (for/id).

Sibling label pattern with a native button
<div id="storage-type">Storage type</div>
<RadioGroup defaultValue="ssd" aria-labelledby="storage-type">
  <div>
    <label for="storage-type-ssd">SSD</label>
    <Radio.Root value="ssd" id="storage-type-ssd" nativeButton render="button">
      <Radio.Indicator />
    </Radio.Root>
  </div>
</RadioGroup>

Native buttons with wrapping labels are supported by using the render callback to avoid invalid HTML, so the hidden input is placed outside the label:

Render callback
<div id="storage-type">Storage type</div>
<RadioGroup defaultValue="ssd" aria-labelledby="storage-type">
  <Radio.Root
    value="ssd"
    nativeButton
    render={(buttonProps) => (
      <label>
        <button {...buttonProps} />
        SSD
      </label>
    )}
  />
</RadioGroup>

Form integration

Use Field and Fieldset for group labeling and form integration:

Using Radio Group in a form
<Form>
  <Field.Root name="storageType">
    <Fieldset.Root render={(props) => <RadioGroup {...props} />}>
      <Fieldset.Legend>Storage type</Fieldset.Legend>
      <Field.Item>
        <Field.Label>
          <Radio.Root value="ssd" />
          SSD
        </Field.Label>
      </Field.Item>
      <Field.Item>
        <Field.Label>
          <Radio.Root value="hdd" />
          HDD
        </Field.Label>
      </Field.Item>
    </Fieldset.Root>
  </Field.Root>
</Form>

API reference

RadioGroup

Provides shared state to a series of radio buttons. Renders a <div> element.

Prop
Type
Default
namestring—
Identifies the field when a form is submitted.string
defaultValueValue—
The uncontrolled value of the radio button that should be initially selected. To render a controlled radio group, use the value prop instead.Value
valueValue—
The controlled value of the radio item that should be currently selected. To render an uncontrolled radio group, use the defaultValue prop instead.Value
onValueChangefunction—
Callback fired when the value changes.((value: Value, eventDetails: RadioGroup.ChangeEventDetails) => void)
formstring—
Identifies the form that owns the radio inputs. Useful when the radio group is rendered outside the form.string
disabledbooleanfalse
Whether the component should ignore user interaction.boolean
readOnlybooleanfalse
Whether the user should be unable to select a different radio button in the group.boolean
requiredbooleanfalse
Whether the user must choose a value before submitting a form.boolean
inputRefRef<HTMLInputElement>—
A ref to access the hidden input element.Ref<HTMLInputElement>
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-disabled-—
Present when the radio group is disabled.-
Attribute
Description
data-disabled
Present when the radio group is disabled.
RadioGroup.State
type RadioGroupState = {
  /** Whether the user should be unable to select a different radio button in the group. */
  readOnly: boolean;
  /** Whether the user must tick a radio button within the group before submitting a form. */
  required: boolean;
  /** Whether the component should ignore user interaction. */
  disabled: boolean;
  /** Whether the field has been touched. */
  touched: boolean;
  /** Whether the field value has changed from its initial value. */
  dirty: boolean;
  /** Whether the field is valid. */
  valid: boolean | null;
  /** Whether the field has a value. */
  filled: boolean;
  /** Whether the field is focused. */
  focused: boolean;
};
RadioGroup.ChangeEventReason
type RadioGroupChangeEventReason = 'none';
RadioGroup.ChangeEventDetails
type RadioGroupChangeEventDetails = {
  /** The reason for the event. */
  reason: 'none';
  /** The native event associated with the custom event. */
  event: Event;
  /** Cancels Base UI from handling the event. */
  cancel: () => void;
  /** Allows the event to propagate in cases where Base UI will stop the propagation. */
  allowPropagation: () => void;
  /** Indicates whether the event has been canceled. */
  isCanceled: boolean;
  /** Indicates whether the event is allowed to propagate. */
  isPropagationAllowed: boolean;
  /** The element that triggered the event, if applicable. */
  trigger: Element | undefined;
};

Root

Represents the radio button itself. Renders a <span> element and a hidden <input> beside.

Prop
Type
Default
value*Value—
The unique identifying value of the radio in a group.Value
nativeButtonbooleanfalse
Whether the component renders a native <button> element when replacing it via the render prop. Set to true if the rendered element is a native button.boolean
disabledboolean—
Whether the component should ignore user interaction.boolean
readOnlyboolean—
Whether the user should be unable to select the radio button.boolean
requiredboolean—
Whether the user must choose a value before submitting a form.boolean
inputRefRef<HTMLInputElement>—
A ref to access the hidden input element.Ref<HTMLInputElement>
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-checked-—
Present when the radio is checked.-
data-unchecked-—
Present when the radio is not checked.-
data-disabled-—
Present when the radio is disabled.-
data-readonly-—
Present when the radio is readonly.-
data-required-—
Present when the radio is required.-
data-valid-—
Present when the radio is in a valid state (when wrapped in Field.Root).-
data-invalid-—
Present when the radio is in an invalid state (when wrapped in Field.Root).-
data-dirty-—
Present when the radio’s value has changed (when wrapped in Field.Root).-
data-touched-—
Present when the radio has been touched (when wrapped in Field.Root).-
data-filled-—
Present when the radio is checked (when wrapped in Field.Root).-
data-focused-—
Present when the radio is focused (when wrapped in Field.Root).-
Attribute
Description
data-checked
Present when the radio is checked.
data-unchecked
Present when the radio is not checked.
data-disabled
Present when the radio is disabled.
data-readonly
Present when the radio is readonly.
data-required
Present when the radio is required.
data-valid
Present when the radio is in a valid state (when wrapped in Field.Root).
data-invalid
Present when the radio is in an invalid state (when wrapped in Field.Root).
data-dirty
Present when the radio’s value has changed (when wrapped in Field.Root).
data-touched
Present when the radio has been touched (when wrapped in Field.Root).
data-filled
Present when the radio is checked (when wrapped in Field.Root).
data-focused
Present when the radio is focused (when wrapped in Field.Root).
Root.State
type RadioRootState = {
  /** Whether the radio button is currently selected. */
  checked: boolean;
  /** Whether the component should ignore user interaction. */
  disabled: boolean;
  /** Whether the user should be unable to select the radio button. */
  readOnly: boolean;
  /** Whether the user must choose a value before submitting a form. */
  required: boolean;
  /** Whether the radio button has been touched (when wrapped in Field.Root). */
  touched: boolean;
  /** Whether the radio button's value has changed from its initial value (when wrapped in Field.Root). */
  dirty: boolean;
  /** Whether the radio button is in a valid state (when wrapped in Field.Root). */
  valid: boolean | null;
  /** Whether the radio button has a value (when wrapped in Field.Root). */
  filled: boolean;
  /** Whether the radio button is focused (when wrapped in Field.Root). */
  focused: boolean;
};

Indicator

Indicates whether the radio button is selected. Renders a <span> element.

Prop
Type
Default
classfunction—
CSS class applied to the element, or a function that returns a class based on the component’s state.JSX.ClassValue | ((state) => JSX.ClassValue)
stylefunction—
Style applied to the element, or a function that returns a style object based on the component’s state.JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)
keepMountedbooleanfalse
Whether to keep the HTML element in the DOM when the radio button is inactive.boolean
renderfunction—
Replace the default element with a tag name, component, or render function.keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)

Data attributes

Name
Type
Default
data-checked-—
Present when the radio is checked.-
data-unchecked-—
Present when the radio is not checked.-
data-disabled-—
Present when the radio is disabled.-
data-readonly-—
Present when the radio is readonly.-
data-required-—
Present when the radio is required.-
data-valid-—
Present when the radio is in a valid state (when wrapped in Field.Root).-
data-invalid-—
Present when the radio is in an invalid state (when wrapped in Field.Root).-
data-dirty-—
Present when the radio’s value has changed (when wrapped in Field.Root).-
data-touched-—
Present when the radio has been touched (when wrapped in Field.Root).-
data-filled-—
Present when the radio is checked (when wrapped in Field.Root).-
data-focused-—
Present when the radio is focused (when wrapped in Field.Root).-
data-starting-style-—
Present when the radio indicator begins animating in.-
data-ending-style-—
Present when the radio indicator is animating out.-
Attribute
Description
data-checked
Present when the radio is checked.
data-unchecked
Present when the radio is not checked.
data-disabled
Present when the radio is disabled.
data-readonly
Present when the radio is readonly.
data-required
Present when the radio is required.
data-valid
Present when the radio is in a valid state (when wrapped in Field.Root).
data-invalid
Present when the radio is in an invalid state (when wrapped in Field.Root).
data-dirty
Present when the radio’s value has changed (when wrapped in Field.Root).
data-touched
Present when the radio has been touched (when wrapped in Field.Root).
data-filled
Present when the radio is checked (when wrapped in Field.Root).
data-focused
Present when the radio is focused (when wrapped in Field.Root).
data-starting-style
Present when the radio indicator begins animating in.
data-ending-style
Present when the radio indicator is animating out.
Indicator.State
type RadioIndicatorState = {
  /** The transition status of the component. */
  transitionStatus: TransitionStatus;
  /** Whether the radio button is currently selected. */
  checked: boolean;
  /** Whether the component should ignore user interaction. */
  disabled: boolean;
  /** Whether the user should be unable to select the radio button. */
  readOnly: boolean;
  /** Whether the user must choose a value before submitting a form. */
  required: boolean;
  /** Whether the radio button has been touched (when wrapped in Field.Root). */
  touched: boolean;
  /** Whether the radio button's value has changed from its initial value (when wrapped in Field.Root). */
  dirty: boolean;
  /** Whether the radio button is in a valid state (when wrapped in Field.Root). */
  valid: boolean | null;
  /** Whether the radio button has a value (when wrapped in Field.Root). */
  filled: boolean;
  /** Whether the radio button is focused (when wrapped in Field.Root). */
  focused: boolean;
};