Capítulo 36 de 51
A mutually-exclusive selection control — Radio.Root always lives inside a RadioGroup (a separate package export), which owns the shared selected value; individual radios only carry their own value, disabled/readOnly/required overrides.
RadioGroup (from @base-ui/react/radio-group) → Radio.Root (from @base-ui/react/radio, repeated) → Radio.Indicator.RadioGroup owns name/value/defaultValue/onValueChange/form — the group-level controlled/uncontrolled value, same convention as CheckboxGroup.Radio.Root.value (required, marked *): identifies which option this radio represents within the group.<label> per option for individual radios, Fieldset.Root render={<RadioGroup />} + Fieldset.Legend for the group's accessible name (ch009 Forms, ch024 Fieldset).loopFocus behavior: Radio Group (like Menu/Menubar items) uses roving tabindex with arrow-key movement between options — default loop is true per the radix-primitives-docs equivalent's documented convention; check current default before assuming.| Part | Notable props | Notable data attributes |
|---|---|---|
RadioGroup | name, value/defaultValue/onValueChange, form, disabled, readOnly, required | data-disabled |
Root | value (required), nativeButton, disabled/readOnly/required | data-checked, data-unchecked, plus shared field-state attributes |
Indicator | keepMounted | same as Root + data-starting-style/data-ending-style |
RadioGroup, never manage mutual exclusivity manually with individual useState per radio — the group owns that state machine.Fieldset.Root render={<RadioGroup />} + Fieldset.Legend for the group's accessible name (ch024) — a bare heading above the group doesn't get programmatically associated.nativeButton/wrapping-label-vs-sibling-label tradeoff.