Skip to main content

Radio

Stable

Mutually exclusive option selection. Arrow keys navigate between items — correct ARIA roving tabindex behavior. Supports vertical and horizontal layouts.

Form elementInteractiveVhyxSeal

Interactive example

Selected: starter
Plan selection

Import

tsx
import { RadioGroup, RadioItem } from '@vhyxui/react'

Orientation

Vertical (default)
Horizontal

Sizes

sm, md, lg

States

With disabled item
Entire group disabled

Props

RadioGroup

PropTypeDefaultDescription
valuestringControlled selected value.
defaultValuestringDefault value for uncontrolled usage.
onValueChange(value: string) => voidCalled when the selected value changes.
disabledbooleanfalseDisables all items in the group.
orientation'horizontal' | 'vertical''vertical'Layout direction of the radio group.
size'sm' | 'md' | 'lg''md'Size of all radio items.
contractPartial<ComponentContract>VhyxSeal contract override.

RadioItem

PropTypeDefaultDescription
value *stringThe value this item represents.
disabledbooleanfalseDisables this specific item independently of the group.
childrenReact.ReactNodeLabel content for this option.

Accessibility

  • Uses roving tabIndex — only the selected (or first) item is in the tab order.
  • role="radiogroup" on the group. role="radio" and aria-checked on each item.
  • Arrow keys navigate between options without leaving the group — Tab exits entirely.
  • Disabled items have aria-disabled="true" and are skipped during arrow key navigation.
  • Always provide aria-label on RadioGroup to name the group for screen readers.
  • Focus ring via :focus-visible — visible on keyboard, hidden on mouse click.

Keyboard navigation

KeyAction
Arrow DownorArrow RightMove focus to the next radio item (wraps around).
Arrow UporArrow LeftMove focus to the previous radio item (wraps around).
SpaceSelect the currently focused item.
TabMove focus out of the radio group entirely.
Shift + TabMove focus to the previous focusable element.

Agent contract

Default VhyxSeal contract shipped with every RadioGroup.

Default contract
{
  "type": "input",
  "intent": "select-option",
  "description": "Captures a single mutually exclusive selection from a set of radio options",
  "requires": [],
  "requiredPermissions": [],
  "consequence": "Updates the selected radio value in the parent form context",
  "affects": [
    "form"
  ],
  "reversible": true,
  "safetyLevel": "low",
  "requiresConfirmation": false,
  "destructive": false,
  "contractVersion": "0.0.1",
  "fingerprint": "vhyxs_65a21959"
}

Theming

Override these CSS tokens to theme RadioGroup.

--vhyx-color-accentSelected indicator fill color
--vhyx-color-accent-hoverIndicator hover color
--vhyx-color-border-strongUnselected border color
--vhyx-color-surfaceUnselected background
--vhyx-radius-fullCircular shape of the radio button
--vhyx-duration-fastSelection animation duration
--vhyx-easing-springSelection animation easing
--vhyx-shadow-focusFocus ring

Examples

T-shirt size selector

Selected: MD
Horizontal size picker

Inside a Field

tsx
<Field name="theme" label="Appearance">
  <RadioGroup
    value={theme}
    onValueChange={setTheme}
    aria-label="Appearance theme"
  >
    <RadioItem value="light">Light</RadioItem>
    <RadioItem value="dark">Dark</RadioItem>
    <RadioItem value="system">System</RadioItem>
  </RadioGroup>
</Field>

Theme picker (live)

Active: system
Appearance setting