> Markdown version of [Radio Button](https://vaadin.com/docs/next/components/radio-button). Section index: [llms.txt](https://vaadin.com/docs/next/components/llms.txt)

# Radio Button

Radio Button Group allows users to select one value among multiple choices.

**Lit** — `radio-button-basic.ts`

```html
<vaadin-radio-group label="Travel class" theme="vertical">
  <vaadin-radio-button value="economy" label="Economy"></vaadin-radio-button>
  <vaadin-radio-button value="business" label="Business"></vaadin-radio-button>
  <vaadin-radio-button value="firstClass" label="First Class"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonBasic.java`

```java
RadioButtonGroup<String> radioGroup = new RadioButtonGroup<>();
radioGroup.setLabel("Travel class");
radioGroup.setItems("Economy", "Business", "First Class");
add(radioGroup);
```

**React** — `radio-button-basic.tsx`

```tsx
<RadioGroup label="Travel class" theme="vertical">
  <RadioButton value="economy" label="Economy" />
  <RadioButton value="business" label="Business" />
  <RadioButton value="firstClass" label="First Class" />
</RadioGroup>
```

## <a id="states"></a>States

### <a id="read-only"></a>Read-Only

Use read-only when content needs to be accessible but not editable. Read-only elements can’t be edited, but they’re part of the tabbing order and can thus receive focus. The content of a read-only input can be selected and copied.

**Lit** — `radio-button-readonly.ts`

```html
<vaadin-radio-group label="Status" theme="vertical" readonly>
  <vaadin-radio-button value="inProgress" label="In progress" checked></vaadin-radio-button>
  <vaadin-radio-button value="done" label="Done"></vaadin-radio-button>
  <vaadin-radio-button value="cancelled" label="Cancelled"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonReadonly.java`

```java
RadioButtonGroup<String> radioGroup = new RadioButtonGroup<>();
radioGroup.setLabel("Status");
radioGroup.setItems("In progress", "Done", "Cancelled");
radioGroup.setValue("In progress");
radioGroup.setReadOnly(true);
add(radioGroup);
```

**React** — `radio-button-readonly.tsx`

```tsx
<RadioGroup label="Status" theme="vertical" readonly>
  <RadioButton value="inProgress" label="In progress" checked />
  <RadioButton value="done" label="Done" />
  <RadioButton value="cancelled" label="Cancelled" />
</RadioGroup>
```

### <a id="disabled"></a>Disabled

Disable a field to mark it as currently unavailable. The disabled state is used for fields that aren’t editable and don’t need to be readable. Disabled elements can’t be focused and may be inaccessible to assistive technologies such as screen readers.

Disabling can be preferable to hiding an element to prevent changes in layout when the element’s visibility changes, and to make users aware of its existence even when it’s currently unavailable.

**Lit** — `radio-button-disabled.ts`

```html
<vaadin-radio-group label="Status" theme="vertical" disabled>
  <vaadin-radio-button value="inProgress" label="In progress" checked></vaadin-radio-button>
  <vaadin-radio-button value="done" label="Done"></vaadin-radio-button>
  <vaadin-radio-button value="cancelled" label="Cancelled"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonDisabled.java`

```java
RadioButtonGroup<String> radioGroup = new RadioButtonGroup<>();
radioGroup.setLabel("Status");
radioGroup.setItems("In progress", "Done", "Cancelled");
radioGroup.setValue("In progress");
radioGroup.setEnabled(false);
add(radioGroup);
```

**React** — `radio-button-disabled.tsx`

```tsx
<RadioGroup label="Status" theme="vertical" disabled>
  <RadioButton value="inProgress" label="In progress" checked />
  <RadioButton value="done" label="Done" />
  <RadioButton value="cancelled" label="Cancelled" />
</RadioGroup>
```

## <a id="orientation"></a>Orientation

The default Radio Button Group orientation depends on the theme: horizontal in Lumo, and vertical in Aura. Vertical orientation is recommended whenever possible, since it’s generally easier for the user to scan a vertical list of options:

**Lit** — `radio-button-vertical.ts`

```html
<vaadin-radio-group label="Status" theme="vertical">
  <vaadin-radio-button value="pending" label="Pending" checked></vaadin-radio-button>
  <vaadin-radio-button value="submitted" label="Submitted"></vaadin-radio-button>
  <vaadin-radio-button value="confirmed" label="Confirmed"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonVertical.java`

```java
RadioButtonGroup<String> radioGroup = new RadioButtonGroup<>();
// Only for Lumo
radioGroup.addThemeVariants(RadioGroupVariant.LUMO_VERTICAL);
radioGroup.setLabel("Status");
radioGroup.setItems("Pending", "Submitted", "Confirmed");
radioGroup.setValue("Pending");
add(radioGroup);
```

**React** — `radio-button-vertical.tsx`

```tsx
<RadioGroup label="Status" theme="vertical">
  <RadioButton value="pending" label="Pending" checked />
  <RadioButton value="submitted" label="Submitted" />
  <RadioButton value="confirmed" label="Confirmed" />
</RadioGroup>
```

In cases where vertical space needs to be conserved, horizontal orientation can be used. However, it’s recommended that there be no more than three options:

**Lit** — `radio-button-horizontal.ts`

```html
<vaadin-radio-group label="Status" theme="horizontal">
  <vaadin-radio-button value="pending" label="Pending" checked></vaadin-radio-button>
  <vaadin-radio-button value="submitted" label="Submitted"></vaadin-radio-button>
  <vaadin-radio-button value="confirmed" label="Confirmed"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonHorizontal.java`

```java
RadioButtonGroup<String> radioGroup = new RadioButtonGroup<>();
// Only for Aura
radioGroup.addThemeVariants(RadioGroupVariant.AURA_HORIZONTAL);
radioGroup.setLabel("Status");
radioGroup.setItems("Pending", "Submitted", "Confirmed");
radioGroup.setValue("Pending");
add(radioGroup);
```

**React** — `radio-button-horizontal.tsx`

```tsx
<RadioGroup label="Status" theme="horizontal">
  <RadioButton value="pending" label="Pending" checked />
  <RadioButton value="submitted" label="Submitted" />
  <RadioButton value="confirmed" label="Confirmed" />
</RadioGroup>
```

In cases where more options are needed, the Select component can be used instead.

## <a id="custom-item-presentation"></a>Custom Item Presentation

Items can be customized to include more than a single line of text:

**Lit** — `radio-button-presentation.ts`

```typescript
<vaadin-radio-group label="Payment method" theme="vertical" .value="${this.value}">
  ${this.items.map(
    (card) => html`
      <vaadin-radio-button .value="${String(card.id)}">
        <label slot="label">
          <vaadin-horizontal-layout theme="spacing">
            <img src="${card.pictureUrl}" alt="${card.name}" style="height: 1em;" />
            <span>${card.accountNumber}</span>
          </vaadin-horizontal-layout>
          <div>Expiry date:${card.expiryDate}</div>
        </label>
      </vaadin-radio-button>
    `
  )}
</vaadin-radio-group>
```

**Flow** — `RadioButtonPresentation.java`

```java
RadioButtonGroup<Card> radioGroup = new RadioButtonGroup<>();
radioGroup.setLabel("Payment method");

List<Card> cards = DataService.getCards();
radioGroup.setItems(cards);
radioGroup.setValue(cards.get(0));
radioGroup.setRenderer(new ComponentRenderer<>(card -> {
    Image logo = new Image(card.getPictureUrl(), card.getName());
    logo.setHeight("1em");
    Span number = new Span(new Text(card.getAccountNumber()));
    Text expiryDate = new Text("Expiry date:" + card.getExpiryDate());

    return new Div(new HorizontalLayout(logo, number),
            new Div(expiryDate));
}));

add(radioGroup);
```

**Flow** — `Card.java`

```java
package com.vaadin.demo.domain;


// tag::snippet[]
public class Card {

    private String name;

    private String accountNumber;

    private String pictureUrl;

    private String expiryDate;

    private String cvv;

    private Integer id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public String getPictureUrl() {
        return pictureUrl;
    }

    public void setPictureUrl(String pictureUrl) {
        this.pictureUrl = pictureUrl;
    }

    public String getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(String expiryDate) {
        this.expiryDate = expiryDate;
    }

    public String getCvv() {
        return cvv;
    }

    public void setCvv(String cvv) {
        this.cvv = cvv;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Card)) {
            return false;
        }
        Card other = (Card) obj;
        return id == other.id;
    }
}
// end::snippet[]
```

**Flow** — `DataService.java`

```java
package com.vaadin.demo.domain;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import tools.jackson.databind.ObjectMapper;

import org.springframework.core.io.ClassPathResource;

public class DataService {

    public static <T> T getItems(Class<T> clazz, String dataFileName) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            InputStream stream = new ClassPathResource("data/" + dataFileName)
                    .getInputStream();
            return mapper.readValue(stream, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static List<Person> getPeople() {
        List<Person> people = Arrays
                .asList(getItems(Person[].class, "people.json"));
        List<String> peopleImages = Arrays
                .asList(getItems(String[].class, "peopleImages.json"));
        for (int index = 0; index < people.size(); index++) {
            String pictureUrl = peopleImages.get(index % peopleImages.size());
            people.get(index).setPictureUrl(pictureUrl);
        }
        return people;
    }

    public static List<Person> getPeople(int count) {
        return getPeople().subList(0, count);
    }

    public static List<Person> getPeople(int count, Integer managerId) {
        List<Person> people = getPeople(managerId);
        return people.subList(0, count);
    }

    public static List<String> getProfessions() {
        List<Person> people = Arrays
                .asList(getItems(Person[].class, "people.json"));
        ArrayList<String> professions = new ArrayList<>();
        for (Person person : people) {
            String profession = person.getProfession();
            if (!professions.contains(profession)) {
                professions.add(profession);
            }
        }
        return professions;
    }

    /**
     * Get employees for a given manager.
     */
    public static List<Person> getPeople(Integer managerId) {
        List<Person> people = new ArrayList<>(getPeople());
        people.removeIf(person -> person.getManagerId() == null
                || !person.getManagerId().equals(managerId));
        return people;
    }

    /**
     * Get all managers.
     */
    public static List<Person> getManagers() {
        List<Person> people = new ArrayList<>(getPeople());
        people.removeIf(person -> !person.isManager());
        return people;
    }

    public static Templates getTemplates() {
        return getItems(Templates.class, "templates.json");
    }

    public static List<Card> getCards() {
        return Arrays.asList(getItems(Card[].class, "cards.json"));
    }

    public static List<Country> getCountries() {
        return Arrays.asList(getItems(Country[].class, "countries.json"));
    }

    public static List<UserPermissions> getUserPermissions() {
        return Arrays
                .asList(getItems(UserPermissions[].class, "permissions.json"));
    }

    public static List<Report> getReports() {
        return Arrays.asList(getItems(Report[].class, "reports.json"));
    }
}
```

**React** — `radio-button-presentation.tsx`

```tsx
<RadioGroup
  label="Payment method"
  theme="vertical"
  value={value.value}
  onValueChanged={(event) => {
    value.value = event.detail.value;
  }}
>
  {items.value.map((card) => (
    <RadioButton value={String(card.id)} key={card.id}>
      <label slot="label">
        <HorizontalLayout theme="spacing">
          <img src={card.pictureUrl} alt={card.name} style={{ height: '1em' }} />
          <span>{card.accountNumber}</span>
        </HorizontalLayout>
        <div>Expiry date:{card.expiryDate}</div>
      </label>
    </RadioButton>
  ))}
</RadioGroup>
```

## <a id="basic-features"></a>Basic Features

The following features, common to most input field components, are supported:

Label

The label is used to identify the input field. It supports plain-text content. In the Lumo theme its length is limited to the width of the field (and truncated with ellipsis), while in the Aura theme labels wrap to multiple lines. [Helpers](#helper) and [Tooltips](#tooltip) can be used to provide additional information that doesn’t fit into the label.

Visible labels are strongly recommended for all input fields. In cases where the built-in label cannot be used, an external element can be associated as the field’s label through the `aria-labelledby` attribute (`setAriaLabelledBy` in Flow). Fields without any visible label should include an invisible label for assistive technologies with the `aria-label` attribute (`setAriaLabel` in Flow).

Helper

Helpers are used to provide additional information that the user may need to enter in the field, such as format requirements or explanations of the field’s purpose below the field.

A [style variant](https://vaadin.com/docs/next/components/radio-button/styling.md#style-variants) is available for rendering the helper above the field.

In addition to plain text, helpers can contain components and HTML elements. However, complex and interactive content is likely to have accessibility issues.

Tooltip

Tooltips are small text pop-ups displayed on hover, and on keyboard-focus. They can be used to provide additional information about a field. This can be useful in situations where an always visible [Helper](#helper) is not appropriate. Helpers are generally recommended in favor of tooltips, though, as they provide much better discoverability and mobile support. See the [Tooltip](https://vaadin.com/docs/next/components/tooltip.md) documentation for more information.

External & Invisible Labels (ARIA)

Visible labels are strongly recommended for all input fields. In situations where the built-in label cannot be used, an external element can be associated as the field’s label through its element `id`. Fields without any visible label should be provided an invisible label for assistive technologies like screen readers.

```html
<!-- Associates external element as label: -->
<label id="external-label">This is the label</label>
<vaadin-radio-group accessible-name-ref="external-label">...

<!-- Invisible label for screen readers: -->
<vaadin-radio-group accessible-name="This is the label">...
```

```java
// Associates external element as label:
NativeLabel label = new NativeLabel("This is the label");
label.setId("external-label");
field.setAriaLabelledBy("external-label");

// Invisible label for screen readers:
field.setAriaLabel("This is the label");
```

**Lit** — `radio-button-group-basic-features.ts`

```typescript
<vaadin-radio-group label="Label" helper-text="Helper text" theme="vertical">
  <vaadin-tooltip slot="tooltip" text="Tooltip text"></vaadin-tooltip>
  <vaadin-radio-button value="1" label="Item 1"></vaadin-radio-button>
  <vaadin-radio-button value="2" label="Item 2"></vaadin-radio-button>
  <vaadin-radio-button value="3" label="Item 3"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonGroupBasicFeatures.java`

```java
RadioButtonGroup<String> field = new RadioButtonGroup<>();
field.setLabel("Label");
field.setHelperText("Helper text");
field.setTooltipText("Tooltip text");
```

**React** — `radio-button-group-basic-features.tsx`

```tsx
<RadioGroup label="Label" helperText="Helper text" theme="vertical">
  <Tooltip slot="tooltip" text="Tooltip text" />

  <RadioButton value="1" label="Item 1" />
  <RadioButton value="2" label="Item 2" />
  <RadioButton value="3" label="Item 3" />
</RadioGroup>
```

## <a id="best-practices"></a>Best Practices

### <a id="group-labels"></a>Group Labels

It’s important to provide labels for Radio Button Groups to distinguish them from each other, especially with multiple adjacent groups.

**Lit** — `radio-button-group-labels.ts`

```html
<vaadin-radio-group label="Job title" theme="vertical">
  <vaadin-radio-button value="analyst" label="Analyst" checked></vaadin-radio-button>
  <vaadin-radio-button value="administrator" label="Administrator"></vaadin-radio-button>
  <vaadin-radio-button value="engineer" label="Engineer"></vaadin-radio-button>
</vaadin-radio-group>

<vaadin-radio-group label="Department" theme="vertical">
  <vaadin-radio-button
    value="engineering"
    label="Engineering"
    checked
  ></vaadin-radio-button>
  <vaadin-radio-button value="humanResources" label="Human Resources"></vaadin-radio-button>
  <vaadin-radio-button value="marketing" label="Marketing"></vaadin-radio-button>
</vaadin-radio-group>
```

**Flow** — `RadioButtonGroupLabels.java`

```java
RadioButtonGroup<String> jobTitleGroup = new RadioButtonGroup<>();
jobTitleGroup.setLabel("Job title");
jobTitleGroup.setItems("Analyst", "Administrator", "Engineer");
jobTitleGroup.setValue("Analyst");
add(jobTitleGroup);

RadioButtonGroup<String> departmentGroup = new RadioButtonGroup<>();
departmentGroup.setLabel("Department");
departmentGroup.setItems("Engineering", "Human Resources", "Marketing");
departmentGroup.setValue("Engineering");
add(departmentGroup);
```

**React** — `radio-button-group-labels.tsx`

```tsx
<RadioGroup label="Job title" theme="vertical" value="analyst">
  <RadioButton value="analyst" label="Analyst" />
  <RadioButton value="administrator" label="Administrator" />
  <RadioButton value="engineer" label="Engineer" />
</RadioGroup>

<RadioGroup label="Department" theme="vertical" value="engineering">
  <RadioButton value="engineering" label="Engineering" />
  <RadioButton value="humanResources" label="Human Resources" />
  <RadioButton value="marketing" label="Marketing" />
</RadioGroup>
```

### <a id="custom-option"></a>Custom Option

To enable the user to enter a custom option instead of picking one from the list, use an "Other" radio button choice at the bottom of the list with an associated Text Field for entry. The field should be hidden or disabled until the "Other" option is selected.

**Lit** — `radio-button-custom-option.ts`

```typescript
<vaadin-vertical-layout>
  <vaadin-radio-group
    label="Payment method"
    theme="vertical"
    .value="${this.value}"
    @value-changed="${(event: RadioGroupValueChangedEvent) => {
      this.value = event.detail.value;
    }}"
  >
    ${this.items.map(
      (card) => html`
        <vaadin-radio-button .value="${String(card.id)}">
          <label slot="label">
            <vaadin-horizontal-layout theme="spacing">
              <img src="${card.pictureUrl}" alt="${card.name}" style="height: 1em;" />
              <span>${card.accountNumber}</span>
            </vaadin-horizontal-layout>
          </label>
        </vaadin-radio-button>
      `
    )}
    <vaadin-radio-button value="-1" label="Other"></vaadin-radio-button>
  </vaadin-radio-group>

  <vaadin-text-field label="Card number" .hidden="${this.value !== '-1'}"></vaadin-text-field>
</vaadin-vertical-layout>
```

**Flow** — `RadioButtonCustomOption.java`

```java
RadioButtonGroup<Card> radioGroup = new RadioButtonGroup<>();
radioGroup.setLabel("Payment method");

List<Card> cards = new ArrayList<>(DataService.getCards());
Card other = new Card();
other.setId(-1);
cards.add(other);
radioGroup.setItems(cards);
radioGroup.setValue(cards.get(0));

radioGroup.setRenderer(new ComponentRenderer<>(card -> {
    if (other.equals(card)) {
        return new Text("Other");
    } else {
        Image logo = new Image(card.getPictureUrl(), card.getName());
        logo.setHeight("1em");
        Span number = new Span(new Text(card.getAccountNumber()));
        return new HorizontalLayout(logo, number);
    }
}));

TextField textField = new TextField("Card number");
textField.setVisible(false);
radioGroup.addValueChangeListener(
        e -> textField.setVisible(other.equals(e.getValue())));

add(radioGroup, textField);
```

**React** — `radio-button-custom-option.tsx`

```tsx
<RadioGroup
  label="Payment method"
  theme="vertical"
  value={value.value}
  onValueChanged={(event) => {
    value.value = event.detail.value;
  }}
>
  {items.value.map((card) => (
    <RadioButton value={String(card.id)} key={card.id}>
      <label slot="label">
        <HorizontalLayout theme="spacing">
          <img src={card.pictureUrl} alt={card.name} style={{ height: '1em' }} />
          <span>{card.accountNumber}</span>
        </HorizontalLayout>
      </label>
    </RadioButton>
  ))}
  <RadioButton value="-1" label="Other" />
</RadioGroup>
```

### <a id="default-value-blank-option"></a>Default Value & Blank Option

It’s recommended that you set the most common option as the default value for Radio Button Groups. Place the default option at the top of the list.

In cases where it’s important that the user make a conscious choice, the Radio Button Group should be blank by default.

In situations where the user isn’t required to select a value, use a "blank" option:

**Lit** — `radio-button-default-value.ts`

```typescript
<vaadin-radio-group label="Repeat" theme="vertical">
  <vaadin-radio-button value="none" label="None" checked></vaadin-radio-button>
  <vaadin-radio-button value="daily" label="Daily"></vaadin-radio-button>
  <vaadin-radio-button value="weekly" label="Weekly"></vaadin-radio-button>
  <vaadin-radio-button value="monthly" label="Monthly"></vaadin-radio-button>
</vaadin-radio-group>
```

**React** — `radio-button-default-value.tsx`

```tsx
<RadioGroup label="Repeat" theme="vertical" value="none">
  <RadioButton value="none" label="None" checked />
  <RadioButton value="daily" label="Daily" />
  <RadioButton value="weekly" label="Weekly" />
  <RadioButton value="monthly" label="Monthly" />
</RadioGroup>
```

### <a id="alternative-to-checkbox"></a>Alternative to Checkbox

Two Radio Buttons can sometimes be a good alternative to a single Checkbox. If the Checkbox doesn’t represent a simple yes/no choice, and its label can’t clearly communicate the meaning of its unchecked state, it’s better to use a Radio Button Group with two options:

**Lit** — `radio-button-checkbox-alternative.ts`

```typescript
<vaadin-checkbox checked>
  <label slot="label">Reply All by default (unchecked state not clear)</label>
</vaadin-checkbox>
<vaadin-radio-group label="Default reply behavior" theme="horizontal">
  <vaadin-radio-button label="Reply" checked></vaadin-radio-button>
  <vaadin-radio-button label="Reply to all"></vaadin-radio-button>
</vaadin-radio-group>
```

**React** — `radio-button-checkbox-alternative.tsx`

```tsx
<Checkbox checked>
  <label slot="label">Reply All by default (unchecked state not clear)</label>
</Checkbox>
<RadioGroup label="Default reply behavior" value="Reply">
  <RadioButton label="Reply" checked value="Reply" />
  <RadioButton label="Reply to all" value="Reply to all" />
</RadioGroup>
```

In a Horizontal Layout, Radio Button Groups also align better with other input fields than a single checkbox.

## <a id="related-components"></a>Related Components

| Component                                                             | Usage Recommendation                                                                                                                   |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| [Select](https://vaadin.com/docs/next/components/select.md)           | A drop-down field for selecting an item from a list of options. Recommended when there is insufficient space for a Radio Button Group. |
| [Combo Box](https://vaadin.com/docs/next/components/combo-box.md)     | A filterable, lazy-loading alternative to Select. Recommended for ten or more items.                                                   |
| [List Box](https://vaadin.com/docs/next/components/list-box.md)       | A scrollable list of options. Supports single and multi-select.                                                                        |
| [Checkbox Group](https://vaadin.com/docs/next/components/checkbox.md) | A corresponding component for multi-select options.                                                                                    |

`E1E617CE-F935-451D-885F-CEF94EC0E53A`
