useSwitch

Provides the behavior and accessibility implementation for a switch component. A switch is similar to a checkbox, but represents on/off values as opposed to selection.

installyarn add react-aria
version3.35.0
usageimport {useSwitch} from 'react-aria'

API#


useSwitch( props: AriaSwitchProps, state: ToggleState, ref: RefObject<HTMLInputElementnull> ): SwitchAria

Features#


There is no native HTML element with switch styling. <input type="checkbox"> is the closest semantically, but isn't styled or exposed to assistive technology as a switch. useSwitch helps achieve accessible switches that can be styled as needed.

  • Built with a native HTML <input> element, which can be optionally visually hidden to allow custom styling
  • Full support for browser features like form autofill
  • Keyboard focus management and cross browser normalization
  • Labeling support for screen readers
  • Exposed as a switch to assistive technology via ARIA

Anatomy#


Low power modeInputLabel

A switch consists of a visual selection indicator and a label. Users may click or touch a switch to toggle the selection state, or use the Tab key to navigate to it and the Space key to toggle it.

useSwitch returns props to be spread onto its input element:

NameTypeDescription
labelPropsLabelHTMLAttributes<HTMLLabelElement>Props for the label wrapper element.
inputPropsInputHTMLAttributes<HTMLInputElement>Props for the input element.
isSelectedbooleanWhether the switch is selected.
isPressedbooleanWhether the switch is in a pressed state.
isDisabledbooleanWhether the switch is disabled.
isReadOnlybooleanWhether the switch is read only.

Selection state is managed by the useToggleState hook in @react-stately/toggle. The state object should be passed as an option to useSwitch.

In most cases, switches should have a visual label. If the switch does not have a visible label, an aria-label or aria-labelledby prop must be passed instead to identify the element to assistive technology.

Example#


This example uses SVG to build the switch, with a visually hidden native input to represent the switch for accessibility. This is possible using the <VisuallyHidden> utility component from @react-aria/visually-hidden. It is still in the DOM and accessible to assistive technology, but invisible. The SVG element is the visual representation, and is hidden from screen readers with aria-hidden.

For keyboard accessibility, a focus ring is important to indicate which element has keyboard focus. This is implemented with the useFocusRing hook from @react-aria/focus. When isFocusVisible is true, an extra SVG element is rendered to indicate focus. The focus ring is only visible when the user is interacting with a keyboard, not with a mouse or touch.

import {useToggleState} from 'react-stately';
import {useFocusRing, useSwitch, VisuallyHidden} from 'react-aria';

function Switch(props) {
  let state = useToggleState(props);
  let ref = React.useRef(null);
  let { inputProps } = useSwitch(props, state, ref);
  let { isFocusVisible, focusProps } = useFocusRing();

  return (
    <label
      style={{
        display: 'flex',
        alignItems: 'center',
        opacity: props.isDisabled ? 0.4 : 1
      }}
    >
      <VisuallyHidden>
        <input {...inputProps} {...focusProps} ref={ref} />
      </VisuallyHidden>
      <svg
        width={40}
        height={24}
        aria-hidden="true"
        style={{ marginRight: 4 }}
      >
        <rect
          x={4}
          y={4}
          width={32}
          height={16}
          rx={8}
          fill={state.isSelected ? 'orange' : 'gray'}
        />
        <circle
          cx={state.isSelected ? 28 : 12}
          cy={12}
          r={5}
          fill="white"
        />
        {isFocusVisible &&
          (
            <rect
              x={1}
              y={1}
              width={38}
              height={22}
              rx={11}
              fill="none"
              stroke="orange"
              strokeWidth={2}
            />
          )}
      </svg>
      {props.children}
    </label>
  );
}

<Switch>Low power mode</Switch>
import {useToggleState} from 'react-stately';
import {
  useFocusRing,
  useSwitch,
  VisuallyHidden
} from 'react-aria';

function Switch(props) {
  let state = useToggleState(props);
  let ref = React.useRef(null);
  let { inputProps } = useSwitch(props, state, ref);
  let { isFocusVisible, focusProps } = useFocusRing();

  return (
    <label
      style={{
        display: 'flex',
        alignItems: 'center',
        opacity: props.isDisabled ? 0.4 : 1
      }}
    >
      <VisuallyHidden>
        <input {...inputProps} {...focusProps} ref={ref} />
      </VisuallyHidden>
      <svg
        width={40}
        height={24}
        aria-hidden="true"
        style={{ marginRight: 4 }}
      >
        <rect
          x={4}
          y={4}
          width={32}
          height={16}
          rx={8}
          fill={state.isSelected ? 'orange' : 'gray'}
        />
        <circle
          cx={state.isSelected ? 28 : 12}
          cy={12}
          r={5}
          fill="white"
        />
        {isFocusVisible &&
          (
            <rect
              x={1}
              y={1}
              width={38}
              height={22}
              rx={11}
              fill="none"
              stroke="orange"
              strokeWidth={2}
            />
          )}
      </svg>
      {props.children}
    </label>
  );
}

<Switch>Low power mode</Switch>
import {useToggleState} from 'react-stately';
import {
  useFocusRing,
  useSwitch,
  VisuallyHidden
} from 'react-aria';

function Switch(props) {
  let state =
    useToggleState(
      props
    );
  let ref = React.useRef(
    null
  );
  let { inputProps } =
    useSwitch(
      props,
      state,
      ref
    );
  let {
    isFocusVisible,
    focusProps
  } = useFocusRing();

  return (
    <label
      style={{
        display: 'flex',
        alignItems:
          'center',
        opacity:
          props
              .isDisabled
            ? 0.4
            : 1
      }}
    >
      <VisuallyHidden>
        <input
          {...inputProps}
          {...focusProps}
          ref={ref}
        />
      </VisuallyHidden>
      <svg
        width={40}
        height={24}
        aria-hidden="true"
        style={{
          marginRight: 4
        }}
      >
        <rect
          x={4}
          y={4}
          width={32}
          height={16}
          rx={8}
          fill={state
              .isSelected
            ? 'orange'
            : 'gray'}
        />
        <circle
          cx={state
              .isSelected
            ? 28
            : 12}
          cy={12}
          r={5}
          fill="white"
        />
        {isFocusVisible &&
          (
            <rect
              x={1}
              y={1}
              width={38}
              height={22}
              rx={11}
              fill="none"
              stroke="orange"
              strokeWidth={2}
            />
          )}
      </svg>
      {props.children}
    </label>
  );
}

<Switch>
  Low power mode
</Switch>

Usage#


The following examples show how to use the Switch component created in the above example.

Default value#

Switches are not selected by default. The defaultSelected prop can be used to set the default state.

<Switch defaultSelected>Wi-Fi</Switch>
<Switch defaultSelected>Wi-Fi</Switch>
<Switch
  defaultSelected
>
  Wi-Fi
</Switch>

Controlled value#

The isSelected prop can be used to make the selected state controlled. The onChange event is fired when the user presses the switch, and receives the new value.

function Example() {
  let [selected, setSelected] = React.useState(false);

  return (
    <>
      <Switch onChange={setSelected}>Low power mode</Switch>
      <p>{selected ? 'Low' : 'High'} power mode active.</p>
    </>
  );
}
function Example() {
  let [selected, setSelected] = React.useState(false);

  return (
    <>
      <Switch onChange={setSelected}>Low power mode</Switch>
      <p>{selected ? 'Low' : 'High'} power mode active.</p>
    </>
  );
}
function Example() {
  let [
    selected,
    setSelected
  ] = React.useState(
    false
  );

  return (
    <>
      <Switch
        onChange={setSelected}
      >
        Low power mode
      </Switch>
      <p>
        {selected
          ? 'Low'
          : 'High'}{' '}
        power mode
        active.
      </p>
    </>
  );
}

Disabled#

Switches can be disabled using the isDisabled prop.

<Switch isDisabled>Airplane Mode</Switch>
<Switch isDisabled>Airplane Mode</Switch>
<Switch isDisabled>
  Airplane Mode
</Switch>

Read only#

The isReadOnly prop makes the selection immutable. Unlike isDisabled, the Switch remains focusable. See the MDN docs for more information.

<Switch isSelected isReadOnly>Bluetooth</Switch>
<Switch isSelected isReadOnly>Bluetooth</Switch>
<Switch
  isSelected
  isReadOnly
>
  Bluetooth
</Switch>

HTML forms#

Switch supports the name and value props for integration with HTML forms.

<Switch name="power" value="low">Low power mode</Switch>
<Switch name="power" value="low">Low power mode</Switch>
<Switch
  name="power"
  value="low"
>
  Low power mode
</Switch>

Internationalization#


RTL#

In right-to-left languages, switches should be mirrored. The switch should be placed on the right side of the label. Ensure that your CSS accounts for this.