Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelog App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip
ARC UI ARC Radiant Components
v3.2 Docs Components Tokens Synthesizer
Getting StartedFrameworksServer Rendering Design TokensThemingTheme SynthesizerTypographyUtilities All ComponentsAccessibilityBrowser SupportChangelogContributingStats App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip

Radio Group

Single-select option group with arrow-key navigation and ARIA radiogroup semantics. Ideal for pricing tiers, settings panels, and any context where exactly one choice must be made from a visible set of options.

Components Radio Group
input hybrid
<arc-radio-group>

Overview

RadioGroup lets users pick exactly one option from a visible set. Unlike Select, which hides choices behind a dropdown, RadioGroup displays every option up front so users can compare them at a glance. This makes it the right choice when the number of options is small (typically two to six) and the decision benefits from side-by-side visibility — pricing tiers, shipping methods, account types, or preference toggles. Under the hood, the component manages focus with arrow-key navigation following the WAI-ARIA radiogroup pattern. Pressing Arrow Down or Arrow Right moves focus to the next option and selects it; Arrow Up or Arrow Left moves backward. This roving-tabindex approach means the entire group occupies a single Tab stop, keeping keyboard navigation fast and predictable. RadioGroup supports both vertical and horizontal orientations. Vertical is the default and works best for options with longer labels or descriptions. Horizontal layout suits compact rows of short labels — for example, a row of size options (S, M, L, XL) or a light/dark theme toggle with more than two states. The component also integrates seamlessly with native form submission through its `name` and `value` properties.

Guidelines

When to use

  • Use RadioGroup when users must choose exactly one option from two to six visible choices
  • Default-select the most common or recommended option to reduce decision friction
  • Use vertical orientation when option labels are longer than a few words
  • Pair with a visible label or heading so users understand what they are choosing
  • Group related radio groups under a shared fieldset or section heading

When not to use

  • Do not use RadioGroup for more than six or seven options — switch to Select or Combobox instead
  • Do not leave the group without a default selection unless the choice is truly optional
  • Do not mix RadioGroup with checkboxes in the same visual row — they imply different selection models
  • Do not use horizontal orientation with long labels — text will wrap awkwardly on small screens
  • Do not disable individual options without explaining why they are unavailable

Features

  • Single-select from a visible set of options — one choice at a time
  • Arrow-key navigation with roving tabindex (single Tab stop)
  • ARIA radiogroup role with automatic `aria-checked` management
  • Vertical and horizontal orientation modes
  • Disabled state for the entire group or individual options
  • Native form integration via name and value properties
  • Fires `arc-change` event on selection with the new value
  • Supports rich option objects with label and value fields

Preview

Starter — Free for individuals Pro — $12/mo for teams Enterprise — Custom pricing

Usage

Layout and styling work without JavaScript via the HTML/CSS versions. Interactive features like events and state management require the Web Component or a framework wrapper.

<script type="module" src="@arclux/arc-ui"></script>

<!-- Vertical (default) -->
<arc-radio-group name="plan" value="starter">
  <arc-radio value="starter">Starter — Free for individuals</arc-radio>
  <arc-radio value="pro">Pro — $12/mo for teams</arc-radio>
  <arc-radio value="enterprise">Enterprise — Custom pricing</arc-radio>
</arc-radio-group>

<!-- Horizontal layout -->
<arc-radio-group name="size" value="md" orientation="horizontal">
  <arc-radio value="sm">Small</arc-radio>
  <arc-radio value="md">Medium</arc-radio>
  <arc-radio value="lg">Large</arc-radio>
  <arc-radio value="xl">X-Large</arc-radio>
</arc-radio-group>

<script>
  document.querySelector('arc-radio-group')
    .addEventListener('arc-change', (e) => {
      console.log('Selected:', e.detail.value);
    });
</script>
import { RadioGroup, Radio } from '@arclux/arc-ui-react';
import { useState } from 'react';

function PricingPlan() {
  const [plan, setPlan] = useState('starter');

  return (
    <RadioGroup
      name="plan"
      value={plan}
      onArcChange={(e) => setPlan(e.detail.value)}
    >
      <Radio value="starter">Starter — Free for individuals</Radio>
      <Radio value="pro">Pro — $12/mo for teams</Radio>
      <Radio value="enterprise">Enterprise — Custom pricing</Radio>
    </RadioGroup>
  );
}

function SizeSelector() {
  return (
    <RadioGroup name="size" value="md" orientation="horizontal">
      <Radio value="sm">Small</Radio>
      <Radio value="md">Medium</Radio>
      <Radio value="lg">Large</Radio>
      <Radio value="xl">X-Large</Radio>
    </RadioGroup>
  );
}
<script setup>
import { RadioGroup, Radio } from '@arclux/arc-ui-vue';
import { ref } from 'vue';

const plan = ref('starter');
</script>

<template>
  <RadioGroup name="plan" :value="plan" @arc-change="plan = $event.detail.value">
    <Radio value="starter">Starter — Free for individuals</Radio>
    <Radio value="pro">Pro — $12/mo for teams</Radio>
    <Radio value="enterprise">Enterprise — Custom pricing</Radio>
  </RadioGroup>
</template>
<script>
  import { RadioGroup, Radio } from '@arclux/arc-ui-svelte';

  let theme = 'dark';
</script>

<RadioGroup name="theme" value={theme} orientation="horizontal"
  on:arc-change={(e) => theme = e.detail.value}>
  <Radio value="light">Light</Radio>
  <Radio value="dark">Dark</Radio>
  <Radio value="auto">System</Radio>
</RadioGroup>
import { Component } from '@angular/core';
import { RadioGroup, Radio } from '@arclux/arc-ui-angular';

@Component({
  imports: [RadioGroup, Radio],
  template: `
    <arc-radio-group name="plan" [value]="plan" (arc-change)="plan = $event.detail.value">
      <arc-radio value="starter">Starter — Free for individuals</arc-radio>
      <arc-radio value="pro">Pro — $12/mo for teams</arc-radio>
      <arc-radio value="enterprise">Enterprise — Custom pricing</arc-radio>
    </arc-radio-group>
  `,
})
export class PricingComponent {
  plan = 'starter';
}
import { RadioGroup, Radio } from '@arclux/arc-ui-solid';
import { createSignal } from 'solid-js';

function ShippingMethod() {
  const [method, setMethod] = createSignal('standard');

  return (
    <RadioGroup name="shipping" value={method()}
      onArcChange={(e) => setMethod(e.detail.value)}>
      <Radio value="standard">Standard — 5-7 business days</Radio>
      <Radio value="express">Express — 2-3 business days</Radio>
      <Radio value="overnight">Overnight — Next business day</Radio>
    </RadioGroup>
  );
}
import { RadioGroup, Radio } from '@arclux/arc-ui-preact';
import { useState } from 'preact/hooks';

function NotificationPreference() {
  const [pref, setPref] = useState('email');

  return (
    <RadioGroup name="notify" value={pref}
      onArcChange={(e) => setPref(e.detail.value)}>
      <Radio value="email">Email notifications</Radio>
      <Radio value="sms">SMS notifications</Radio>
      <Radio value="push">Push notifications</Radio>
      <Radio value="none">No notifications</Radio>
    </RadioGroup>
  );
}
<!-- arc-radio-group is interactive — requires JS -->
<arc-radio-group></arc-radio-group>
<!-- arc-radio-group is interactive — requires JS -->
<arc-radio-group></arc-radio-group>

API

value string ''
The currently selected value. Must match one of the child arc-radio value attributes. Setting this property programmatically updates the visual selection and the internal aria-checked state.
name string ''
The form field name submitted with the selected value. Required for native form integration — without it, the selection will not appear in FormData.
disabled boolean false
When true, disables all options in the group. The component becomes non-interactive: arrow-key navigation is suppressed, click events are ignored, and the group is excluded from the Tab order.
size 'sm' | 'md' | 'lg' 'md'
Controls the radio button and label size.
orientation 'vertical' | 'horizontal' 'vertical'
Controls the layout direction of the radio options. Vertical stacks options top-to-bottom and maps Arrow Up/Down to navigation. Horizontal places options in a row and maps Arrow Left/Right.
formAssociated boolean true
properties object { required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, }
Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
autoValidates boolean true
Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
form
validity
validationMessage
required boolean false
readonly boolean false

Events

arc-change detail: { value: string }
Fired when the selected radio value changes

Radio

<arc-radio>

Individual radio option rendered inside a RadioGroup. Each Radio represents a single selectable choice with its own label and value. Can be independently disabled while the rest of the group remains interactive.

label
value string ''
The value submitted when this option is selected. Must be unique within the parent RadioGroup.
disabled boolean false
When true, dims this individual option and removes it from keyboard navigation. The option cannot be selected by click or arrow keys.

See Also