# p-radio-group

The `p-radio-group` component is a versatile custom form element that enables the selection of a single option.

> **Attention**
>
> The `blur` event is emitted whenever the focused `p-radio-group-option` changes — not only when the entire `p-radio-group` loses focus. This means that switching between options within the same group will also trigger a `blur` event for the previously focused option.

Options are slotted using the `p-radio-group-option` component. Each option needs an assigned `string` or `number` value. The `p-radio-group` value matches an option strictly by type and value, so a number does not match the equivalent string. `undefined` and `null` are reserved for the radio group's unselected state and are not valid option values. Initial selection states can only be achieved using the `value` property on the `p-radio-group` component. If an option should be visible but not selectable, it can be disabled using the `disabled` attribute.

A `label` is a caption which informs the user what information a particular form field is asking for. The `p-radio-group` component can be used with or without a label, but it's recommended to keep the label visible for better accessibility whenever possible. When used without a label, it's best practice to provide a descriptive label text for screen readers.

## Usage

### Do:

- Use to enable a user to select one value from a predefined list. Use as soon as two options are mutually exclusive.
- Use [Checkbox](../p-checkbox/p-checkbox.md) instead if there is only one option.
- Use when the user should see all available options.
- Consider using the [Segmented Control](../p-segmented-control/p-segmented-control.md) for 5 options or fewer.
- Keep the label text short and descriptive (one or two words).
- Select one option by default in a group of radio buttons, except for cases where a pre-selection can lead to wrong assumptions.
- Align radio buttons with more than 2 items vertically left-aligned.

### Don't:

- Don't use a radio Button for a large number of items (≥ 7), use the [Select](../p-select/p-select.md) or [Segmented Control](../p-segmented-control/p-segmented-control.md) component instead.

---

## Related Components

- [Checkbox](../p-checkbox/p-checkbox.md)
- [Segmented Control](../p-segmented-control/p-segmented-control.md)
- [Select](../p-select/p-select.md)

## Accessibility support

### Keyboard

| Key / state | Function |
| --- | --- |
| `Tab`, `Shift-Tab` | Moves focus to the next (or previous) focusable element. |
| `Arrow left`, `Arrow right`, `Down Arrow` `Up Arrow` | Activates and moves focus to the next/prev radio button in a group. |

### ARIA enhancements

#### Internal **ARIA** that is managed by the component:

| ARIA | Usage |
| --- | --- |
| `aria-labelledby="IDREF"` | Defines the accessible name. |
| `aria-describedby="IDREF"` | Defines the accessible description combined with the status message. |
| `role="status"`, `role="alert"` | When `message` and `state` or `loading` prop change, the status message is announced. |
| `aria-hidden="true"` | If `required` attribute is set, the visible asterisk indicator is hidden from assistive technologies. |
| `aria-invalid="true"` | If `error` state is set. |
| `aria-required="true"` | Indicates if the element is required. |

## Development considerations

### Disabled state

In general, avoid using the `disabled` state. Disabled elements are no longer focusable and may be missed by screen reader users. They can also confuse sighted users if the reason they are disabled is not made clear.

### Common do's and don'ts

- **Do** provide a clear group `label` that describes the choice being made.
- **Do** use meaningful option labels so each choice is understandable out of context.
- **Do** use the component state/message API so validation and status changes are announced.
- **Don't** rely on adjacent text instead of the group `label` prop.
- **Don't** use `hide-label` without an accessible name.

## Integration examples

See [accessibility integration examples](./accessibility.md) for paired anti-pattern and recommended implementations.

## Tests

### Automated

| Technology | Support |
| --- | --- |
| AXE-Core (WCAG 2.2 AA, Best-Practice) | ✅ |
| High-Contrast Mode (light/dark) | ✅ |
| Text-Zoom (200%) | ✅ |

### Manual

| Technology | Support |
| --- | --- |
| Keyboard | ✅ |
| Screen reader (VoiceOver, NVDA) | ⚠️(Note: VoiceOver/NVDA do not announce the number of options correctly when used with Safari or Chrome) |

## API

### Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `compact` | `boolean` | `false` | Reduces the spacing between radio options for use in dense layouts where vertical space is limited. |
| `description` | `string` | `''` | Sets a supplementary description displayed below the label to give users additional guidance about the radio group. |
| `direction` | `'row'` `'column'`<br>`BreakpointCustomizable<RadioGroupDirection>` | `'column'` | Sets the layout direction of the radio options. Use `column` to stack them vertically or `row` to arrange them horizontally. Supports responsive breakpoint values. |
| `disabled` | `boolean` | `false` | Disables all radio options in the group, preventing selection and excluding the value from form submissions. |
| `form` | `string` | `undefined` | Associates the radio group with a form element by its ID when the group is not a direct descendant of that form. |
| `hideLabel` | `boolean`<br>`BreakpointCustomizable<boolean>` | `false` | Hides the visible label while keeping it accessible to screen readers. Supports responsive breakpoint values. |
| `label` | `string` | `''` | Sets the visible label text displayed above the radio group to identify the group's purpose. |
| `loading` _(experimental)_ | `boolean` | `false` | @experimental Disables all radio options and shows a spinner to indicate a background loading operation. |
| `message` | `string` | `''` | Sets the validation feedback message displayed below the radio group when `state` is `success` or `error`. |
| `name` _(required)_ | `string` | `undefined` | Sets the shared name attribute for all radio buttons in the group, grouping them for mutually exclusive selection. |
| `required` | `boolean` | `false` | Marks the radio group as required so the form cannot be submitted until one option is selected. |
| `state` | `'none'` `'error'` `'success'` | `'none'` | Sets the validation state of the radio group, controlling its visual appearance and feedback message style (`none`, `success`, `error`). |
| `value` | `string | number | null` | `undefined` | Sets the currently selected value that pre-selects the matching radio option and reflects user changes. Matches an option strictly by type and value, meaning string or number only match an option whose value has the same type and equal value. Use undefined or null for no preselection; these values never match an option because every option requires a string or number value. Please note that FormData always serializes values as strings, so when participating in a native (uncontrolled) form a number value is restored as string via formStateRestoreCallback and will no longer strictly match a number-typed option. This limitation only applies to native form state restoration; in controlled forms (where the consumer manages value directly via the change event), the number type is preserved end-to-end. |

### Events

| Event | Type | Description |
| --- | --- | --- |
| `blur` | `CustomEvent<void>` | Emitted when the radio group loses focus after the user interacts with it, useful for triggering validation on blur. |
| `change` | `CustomEvent<RadioGroupChangeEventDetail>`<br>`Event` | Emitted when the user selects a different option, carrying the new value and the native event in the event detail. |

### Slots

| Slot | Required | Allowed tag names | Description |
| --- | --- | --- | --- |
| `label` | no | — | Shows a label. Only [phrasing content](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content) is allowed. |
| `label-after` | no | — | Places additional content after the label text (for content that should not be part of the label, e.g. external links or `p-popover`). |
| `description` | no | — | Shows a description. Only [phrasing content](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content) is allowed. |
| _(default)_ | no | — | Default slot for the p-radio-group-option tags. |
| `message` | no | — | Shows a state message. Only [phrasing content](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content) is allowed. |

## Sub-components

These tags are only valid inside the parent(s) listed under each one — often this component, but some are shared and list a different parent (e.g. a tag documented here via a common ancestor). Their APIs come from the same authoritative `component-meta` as the parent above.

### `p-radio-group-option`

Allowed parent: `p-radio-group`.

#### Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | Prevents this option from being selected and excludes its value from form submissions while it is disabled. |
| `label` | `string` | `undefined` | Sets the visible label text displayed next to the radio button that the user reads to identify the option. |
| `loading` _(experimental)_ | `boolean` | `false` | @experimental Disables this option and shows a spinner to indicate that this particular option is in a loading state. |
| `value` _(required)_ | `string | number` | `undefined` | Sets the required option value. Must be a string or number and is selected when it strictly matches the parent `p-radio-group` value by type and value. |

#### Slots

| Slot | Required | Allowed tag names | Description |
| --- | --- | --- | --- |
| `label` | no | — | Shows a label. Only [phrasing content](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content) is allowed. |
| `label-after` | no | — | Places additional content after the label text (for content that should not be part of the label, e.g. external links or `p-popover`). |

## Examples

| Example | When to use | File |
| --- | --- | --- |
| Default | Minimal default configuration. | [./examples/Default.ts](./examples/Default.ts) |
| Form | The `p-radio-group` can be integrated into a form in two ways: **controlled** or **uncontrolled**, depending on your needs. | [./examples/Form.ts](./examples/Form.ts) |
| Slots | Use named slots when `label` or `message` need markup such as a link. | [./examples/Slots.ts](./examples/Slots.ts) |
