# Switch

A cross-platform toggle for binary on/off settings; a visual alternative to a checkbox.
<!-- BEGIN:xui-mcp-instructions:switch -->
Use a switch when a user can choose only one of the options: on or off. Use when items in a list can be independently controlled.

### When to use

- To toggle a single setting, feature, or preference on or off
- When each item in a list can be independently enabled or disabled
- When the change takes effect immediately, without requiring a *"Save"* or *"Submit"* action

### When not to use
- When the user must choose between more than two states — use a Select or Radio group
- When the change requires confirmation before taking effect — use a Checkbox with a submit action instead
- When the options are not binary (on/off) — use a ToggleButtonGroup

### Content guidelines

Label should describe the feature or setting being toggled, written as a noun or noun phrase: *"Email notifications"*, *"Auto-renew"*, *"Dark mode"*.

Avoid labelling the switch with the action: not *"Enable notifications"* — just *"Notifications"*.

Description should explain the impact of the toggle in plain language. Keep it to one sentence. It may contain a link where the user needs somewhere to read more (a privacy policy, a docs page) — keep it to a single link, and keep the sentence readable without it.

Error message should be specific and constructive: *"This setting cannot be changed while another process is running"* — not just *"Error"*.

### Behaviour guidelines

The switch should take effect immediately on toggle, without requiring a confirmation step. If the action has significant consequences, show a confirmation dialog before applying.

In a list of switches, each one is independent — toggling one should never affect the others unless there is an explicit dependency (e.g. a parent/child relationship between settings).

Disabled switches should preserve their current value visibly so the user understands the current state even if they cannot change it. Provide a tooltip explaining why the switch is disabled.

Avoid using a switch to replace a Checkbox in a form that is submitted later — switches imply immediate effect.

### Accessibility

The switch should render as role=*"switch"* with aria-checked=*"true"* or aria-checked=*"false"* to communicate its state to screen readers.

The Description text should be linked via aria-describedby so screen readers announce it alongside the label.

The Error message must be linked via aria-describedby so it is announced on focus in the Error state.

Disabled switches should remain focusable (aria-disabled=*"true"* rather than the HTML disabled attribute) to allow tooltip display and screen reader announcement.

Ensure the focus ring is visible for keyboard users — the component does not define a focus state visually; this must be implemented in code.

The minimum touch target for interactive elements is 44×44px. Sizes S (16px tall) and M (18px tall) require additional padding in touch contexts to meet this threshold.
<!-- END:xui-mcp-instructions:switch -->

## Installation

```bash
npm install @xsolla/xui-switch
```

## Imports

```tsx
import { Switch } from "@xsolla/xui-switch";
import type { SwitchProps } from "@xsolla/xui-switch";
```

## Quick start

```tsx
const [enabled, setEnabled] = useState(false);

<Switch
  checked={enabled}
  onValueChange={setEnabled}
  label="Enable notifications"
/>;
```

## API Reference

### `<Switch>`

| Prop             | Type                                           | Default     | Description                                                                                                   |
| ---------------- | ---------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------- |
| `testID`         | `string`                                       | —           | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
| `checked`        | `boolean`                                      | `false`     | Whether the switch is on.                                                                                     |
| `size`           | `'sm' \| 'md' \| 'lg' \| 'xl'`                 | `'md'`      | Size of the switch.                                                                                           |
| `state`          | `'default' \| 'hover' \| 'disable' \| 'error'` | `'default'` | Visual state.                                                                                                 |
| `disabled`       | `boolean`                                      | `false`     | Disable the switch.                                                                                           |
| `label`          | `string`                                       | —           | Label text shown next to the switch.                                                                          |
| `labelPosition`  | `'left' \| 'right'`                            | `'right'`   | Position of the label.                                                                                        |
| `description`    | `React.ReactNode`                              | —           | Description content below the label. Accepts a plain string or rich content such as a link — see [Rich description](#rich-description). |
| `errorLabel`     | `string`                                       | —           | Error message shown when `state === 'error'`.                                                                 |
| `onValueChange`  | `(value: boolean) => void`                     | —           | Fired when toggled.                                                                                           |
| `ariaLabel`      | `string`                                       | —           | Accessible label for screen readers.                                                                          |
| `ariaLabelledBy` | `string`                                       | —           | ID of an element that labels the switch.                                                                      |

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

## Examples

### With description

```tsx
const [darkMode, setDarkMode] = useState(false);

<Switch
  checked={darkMode}
  onValueChange={setDarkMode}
  label="Dark mode"
  description="Enable dark theme across the application"
/>;
```

### Rich description

`description` takes any React node, so it can carry a link or other inline
markup:

```tsx
const [analytics, setAnalytics] = useState(false);

<Switch
  checked={analytics}
  onValueChange={setAnalytics}
  label="Product analytics"
  description={
    <>
      Share anonymous usage data. Read our{" "}
      <a href="https://xsolla.com/privacypolicy" target="_blank" rel="noreferrer">
        privacy policy
      </a>{" "}
      first.
    </>
  }
/>;
```

The whole switch row is one press target, so the component ignores presses that
originate on interactive content — `a[href]`, `button`, `[role="link"]`,
`[role="button"]`. Following a link in the description therefore does **not**
toggle the switch, on click or on Enter. Presses on plain (non-interactive)
description text still toggle, as before.

This guard is a web behaviour: on React Native the press event carries a node
handle rather than a DOM element, so it is skipped and every press toggles.
Avoid embedding interactive content in `description` on native.

### Label positions

```tsx
<Switch label="Label on the right" labelPosition="right" />
<Switch label="Label on the left" labelPosition="left" />
```

### Sizes

```tsx
<Switch size="sm" label="Small" checked />
<Switch size="md" label="Medium" checked />
<Switch size="lg" label="Large" checked />
<Switch size="xl" label="Extra large" checked />
```

### Error state

```tsx
<Switch
  checked={false}
  state="error"
  label="Required setting"
  errorLabel="This setting must be enabled to continue"
/>
```

### Disabled

```tsx
<Switch state="disable" label="Disabled off" />
<Switch state="disable" checked label="Disabled on" />
```

## Accessibility

- Uses `role="switch"` and `aria-checked` to reflect state.
- Space and Enter toggle the switch.
- `description` and `errorLabel` are linked via `aria-describedby`. A rich
  `description` is announced as its flattened text content, so keep link text
  meaningful on its own (*"privacy policy"*, not *"click here"*).
- A link inside `description` stays independently focusable and activatable —
  Enter on the link follows it rather than toggling the switch.
- Disabled state is announced to assistive technology.
