> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Radio - Web

> Radio buttons are form elements for selecting one option from a set of choices.

> [!NOTE]
>
> For development, import `Radio` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=207-2915).

![](https://mintcdn.com/servicetitan/H5FwKyiqhPVZ1UJQ/images/docs/web/components/shared/radio-group-overview.png?fit=max\&auto=format\&n=H5FwKyiqhPVZ1UJQ\&q=85\&s=af6f547acca3adda1c5484e3308ae78a)

## Anatomy

The Radio uses two primary elements to help users select one option from a set of choices, the radio itself, and its corresponding label.

![Anatomy of a Radio
Group](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/radio/design/anatomy-of-a-radio-group.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=ec500ad8507ffe21bec91f336bd8148f)

1. Radio button
2. Label

## Options

Configure the Radio to accommodate the following scenarios:

### States

#### Default

radio-defaultchecked

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-defaultchecked?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Notification Frequency">
      <Radio name="ex1" value="daily" label="Daily" />
      <Radio name="ex1" value="weekly" label="Weekly" defaultChecked />
      <Radio name="ex1" value="monthly" label="Monthly" />
    </Radio.Group>
  );
}

export default App;
```

#### Disabled

radio-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-disabled?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Notification Frequency">
      <Radio name="ex2" value="daily" label="Daily" disabled />
      <Radio name="ex2" value="weekly" label="Weekly" disabled defaultChecked />
      <Radio name="ex2" value="monthly" label="Monthly" disabled />
    </Radio.Group>
  );
}

export default App;
```

### Description

radio-description

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-description?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return <Radio label="Label" description="This is a description" />;
}

export default App;
```

Descriptions provide information to help the user avoid errors.

## Behavior

The Radio uses standard form control behaviors.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoChoose from small option sets | Use Radio buttons to choose between small sets of items, usually 5 or fewer. Since all options are always visible, users can quickly scan and compare them. You can provide additional context through helper text or a help tooltip to help users choose accurately. |
| DoUse for 2–7 options | Generally use Radio buttons when there are 2–7 options. |
| DoSimple in-form selection lists | For simple selection lists in a form, use a Radio. |
| DoControls that need explicit submission | Radio buttons are form controls that require explicit submission. |
| DoUse in form layouts for multiple options | Radio buttons let users choose from two or more options and require form submission. Use Radio buttons in form layouts when users select between multiple options. |
| DoMutually exclusive single-select | Use Radio buttons to select a single option from mutually exclusive choices. |
| DoGroup related Radios | Group related Radio buttons together in a Radio Group. In most cases, include a label. |
| DoProvide a default option | Provide a default option in a Radio Group when possible. This signals that a selection is required, can expedite tasks, and guides users toward a recommended option. Source: [Nielsen Norman](https://www.nngroup.com/articles/radio-buttons-default-selection/). |
| DoReveal a Text Field for "Other" | When a Radio Group includes an "Other" option, selecting it should reveal a Text Field so users can enter custom text. |
| Don'tUse for 8+ options | Use a [Combobox](https://anvil.servicetitan.com/docs/components/combobox/web.md) instead when there are 8 or more options. |
| Don'tOverlay selection lists | For selection lists that need to be an overlay, use a [Listbox](https://anvil.servicetitan.com/docs/components/listbox/web.md) with a Combobox instead. |
| Don'tUse when selection should apply immediately | Segmented Controls apply selection immediately to the UI; use one of those instead when immediate effect is needed. |
| Don'tUse for an immediate two-state toggle | A Switch toggles between two states and takes effect immediately; use a Switch instead for that. |
| Don'tUse for multi-select choices | Use Checkboxes instead to make zero to many selections from available options. |
| Don'tDefault when it risks unexpected effects | Avoid a default selection when it could lead to unexpected downstream effects. |

## Content

Radio content should clearly communicate options and provide context for informed decisions.

### The Radio Group's title and description communicate its main purpose

Titles should clearly communicate the Radio Group's purpose at a glance. Descriptions provide additional context about the decision users need to make.

radio-legend-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-legend-do?file=src/App.tsx)

```tsx lines theme={null}
import { Radio, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group
      legend={
        <Text variant="headline" el="h3" size="small">
          Assign geofence tracking to vehicles
        </Text>
      }
      description="A geofence can be applied to a selection of individual vehicles or assigned to one or more vehicle groups."
    >
      <Radio
        name="c1"
        value="a"
        label="Apply to individual vehicles"
        defaultChecked
      />
      <Radio name="c1" value="b" label="Apply to vehicle groups" />
    </Radio.Group>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

radio-legend-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-legend-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Select vehicles to monitor">
      <Radio
        name="c1"
        value="a"
        label="Apply to individual vehicles"
        defaultChecked
      />
      <Radio name="c1" value="b" label="Apply to vehicle groups" />
    </Radio.Group>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

### Use descriptive Radio input labels

Users rely on labels to understand what each Radio input controls. Use positive, active wording for Radio input labels.

Use sentence case (capitalize only the first word and proper nouns) so control labels are easy to scan. Do not use periods for short phrases or single sentences.

"Leaky bathroom faucet"\
"Clogged kitchen drain"\
"No hot water"\
"Slow shower drain"\
"Overflowing toilet"

> [!TIP]
>
> **Do**

"Fix water problem"\
"Plumbing emergency"\
"Water not working"\
"Broken pipe"\
"Plumbing issue"

> [!CAUTION]
>
> **Don't**

### Give users the information they need to make an informed decision

When a label doesn't provide enough context, use descriptions to give users the relevant details they need to decide.

Don't add descriptions when unneeded or solely to match other inputs.

Use sentence case, and only include period if more than one sentence is used.

Follow the inline help content guidelines.

radio-description-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-description-do?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Update auto-renew setting">
      <Radio
        name="c3"
        id="ex-c3-1"
        value="a"
        label="Enabled"
        description="Agreement will be automatically renewed on its end date"
      />
      <Radio
        name="c3"
        id="ex-ce-2"
        value="b"
        label="Disabled"
        description="Agreement will expire on its end date unless renewed manually"
      />
    </Radio.Group>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

radio-description-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-description-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Update auto-renew setting">
      <Radio
        name="c3"
        id="ex-c3-1"
        value="a"
        label="Enabled"
        description="Agreement will automatically renew"
      />
      <Radio
        name="c3"
        id="ex-ce-2"
        value="b"
        label="Disabled"
        description="Agreement will not automatically renew"
      />
    </Radio.Group>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

## Keyboard Interaction

Navigate the Radio using standard keyboard controls.

| Key | Interaction |
| - | - |
| Arrow keys | Navigate and selects Radios |

### Accessibility

* Include Radio buttons in a Radio Group with multiple mutually exclusive options.
* Provide a visible label for each Radio button.
* When a Radio button has an error, include helper text to describe the error.

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

## Code

**Implementation**

radio-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Choose fruit(s)" required onChange={console.log}>
      <Radio name="fruit" value="Apple" label="Apple" />
      <Radio name="fruit" value="Banana" label="Banana" />
      <Radio name="fruit" value="Strawberry" label="Strawberry" />
    </Radio.Group>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { Radio } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Radio.Group Group legend="Preferred method of communication">
      <Radio name="comm" label="Email" defaultChecked />
      <Radio name="comm" label="Phone" />
      <Radio name="comm" label="Text" />
    </Radio.Group>
  );
}
```

### Basic radio input

The `Radio` component accepts many of the same props as an HTML `input` with `type="radio"`, such as `name`, `value`, `checked`, and `defaultChecked`.

radio-html

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-html?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return <Radio value="value-default" label="Default" />;
}

export default App;
```

### Controlling radio state

Use the `checked` and `onClick` props to manually control the state of a radio.

radio-controlled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-controlled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Radio, type RadioState } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [list, setList] = useState([
    {
      label: "Email",
      checked: false,
    },
    {
      label: "Text",
      checked: true,
    },
  ]);

  const handleUpdate = (_state: RadioState | undefined, index: number) => {
    setList((prev) =>
      prev.map((item, i) => ({
        label: item.label,
        checked: index === i,
      })),
    );
  };

  return (
    <Radio.Group legend="Preferred communication method" required>
      {list.map((item, i) => {
        return (
          <Radio
            key={i}
            checked={item.checked}
            label={item.label}
            onChange={(_e, state) => handleUpdate(state, i)}
          />
        );
      })}
    </Radio.Group>
  );
}

export default App;
```

### Using radio groups

The `Radio.Group` component acts as a wrapper around a collection of `Radio` components.

radio-radiogroup

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-radiogroup?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Choose a taco" required>
      <Radio name="taco" value="asada" label="Carne Asada" />
      <Radio name="taco" value="pollo" label="Pollo" />
    </Radio.Group>
  );
}

export default App;
```

## React Accessibility

* The `Radio` component's `label` prop allows for any `ReactElement` to be passed in, but the `label` should always be populated with some text content that is screen-reader accessible.

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

### Markdown in legends

The `Radio.Group` `legend` prop supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

radio-group-markdownlabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-group-markdownlabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Flex, Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="6">
      <Radio.Group legend="**Bold** legend">
        <Radio name="bold" value="a" label="Option A" />
        <Radio name="bold" value="b" label="Option B" />
      </Radio.Group>
      <Radio.Group legend="*Italic* legend">
        <Radio name="italic" value="a" label="Option A" />
        <Radio name="italic" value="b" label="Option B" />
      </Radio.Group>
      <Radio.Group legend="***Bold and italic*** legend">
        <Radio name="bolditalic" value="a" label="Option A" />
        <Radio name="bolditalic" value="b" label="Option B" />
      </Radio.Group>
      <Radio.Group legend="==Highlight== legend">
        <Radio name="highlight" value="a" label="Option A" />
        <Radio name="highlight" value="b" label="Option B" />
      </Radio.Group>
      <Radio.Group legend="`Code` legend">
        <Radio name="code" value="a" label="Option A" />
        <Radio name="code" value="b" label="Option B" />
      </Radio.Group>
    </Flex>
  );
}

export default App;
```

### Hide the legend

Use `hideLabel` on `Radio.Group` to visually hide the legend. The `legend` string is kept as a screen-reader-only `<legend>` element so the group remains accessible — any inline markdown is stripped to plain text.

radio-group-hidelabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/radio-group-hidelabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Radio } from "@servicetitan/anvil2";

function App() {
  return (
    <Radio.Group legend="Payment method" hideLabel flexDirection="row">
      <Radio name="payment" value="cc" label="Credit card" />
      <Radio name="payment" value="bank" label="Bank transfer" />
      <Radio name="payment" value="paypal" label="PayPal" />
    </Radio.Group>
  );
}

export default App;
```

**Radio Props**

```tsx theme={null}
<Radio
  name="option"
  label="Option 1"
  value="option1"
  defaultChecked={false}
  checked={false}
  error={false}
  icons={{ checked: CheckIcon, unchecked: UncheckIcon }}
  onChange={(e, state) => console.log(state)}
/>
```

## `Radio` Props

In addition to the props listed below, the `Radio` component can accept any valid HTML `input` props.

**onChange** `(e: ChangeEvent, state: \{ checked: boolean; value: string | string[] | number \}) =\> void` *required*

Callback when the radio is changed.

**checked** `boolean` (default: false)

Controlled checked state.

**defaultChecked** `boolean` (default: false)

Uncontrolled checked state.

**description** `string`

Description text to display below the radio.

**error** `boolean`

Error state for the radio.

**icons** `\{ checked?: Svg; unchecked?: Svg \}`

Overrides for default icons.

**label** `ReactElement | string`

The `label` should have accessible `string` content.

**Radio.Group Props**

```tsx theme={null}
<Radio.Group
  legend="Select an option"
  moreInfo="Additional information"
  required={false}
>
  <Radio name="option" label="Option 1" />
  <Radio name="option" label="Option 2" />
</Radio.Group>
```

## `Radio.Group` Props

In addition to the props listed below, the `Radio.Group` component can accept any valid HTML `fieldset` props.

**legend** `ReactElement | string` *required*

The legend for the radio group. Supports inline markdown formatting.

> [!WARNING]
>
> Passing `ReactElement` is deprecated — use a plain string with inline markdown instead.

**hideLabel** `boolean` (default: false)

Visually hides the legend while keeping it accessible to screen readers.

**legendProps** `Omit\<FieldLabelProps, "el"\>`

Additional props to pass to the legend [FieldLabel](https://anvil.servicetitan.com/docs/components/field-label/code) component. Supports the `aiMark` prop for displaying [AI-powered field indicators](https://anvil.servicetitan.com/docs/utilities/web/ai-marks.md).

**required** `boolean`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.