Skip to main content
react-md

Switch

Switches toggle the state of a single item on or off and are the preferred way to adjust settings on mobile.

Use switches to:

Simple Switch

A switch can be created using the Switch component. A label should normally be provided for accessibility purposes.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function SimpleSwitch(): ReactElement {
  return (
    <Form className={box()}>
      <Switch label="Label" />
      <Switch label="Label" defaultChecked />
      <Switch label="Disabled" disabled />
    </Form>
  );
}

Press Enter to start editing.

Icon After Label

The label can be placed before the switch icon by enabling the iconAfter prop.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function IconAfterLabel(): ReactElement {
  return (
    <Form className={box()}>
      <Switch label="Label" iconAfter />
      <Switch label="Label" iconAfter />
    </Form>
  );
}

Press Enter to start editing.

Stacked Label

The label can be stacked above or below the switch by enabling the stacked prop.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function StackedSwitch(): ReactElement {
  return (
    <Form className={box({ align: "start", stacked: true })}>
      <Switch label="Stacked" stacked iconAfter />
      <Switch label="Stacked" stacked />
    </Form>
  );
}

Press Enter to start editing.

Switch Sizes

The Switch will automatically scale the label and switch toggle based on the current font-size. Apply the font-size only to the trackStyle/trackClassName if only the switch toggle should change size.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function SwitchSizes(): ReactElement {
  return (
    <Form className={box({ align: "start", stacked: true })}>
      <Switch label="Small" style={{ fontSize: "0.75rem" }} />
      <Switch label="Medium" style={{ fontSize: "1.25rem" }} />
      <Switch label="Large" style={{ fontSize: "2rem" }} />
      <Switch label="Toggle Only" trackStyle={{ fontSize: "1.5rem" }} />
    </Form>
  );
}

Press Enter to start editing.

Switch States

The Switch also has different states that can be applied. The most common will be the error or disabled states but also supports active.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function SwitchStates(): ReactElement {
  return (
    <Form className={box()}>
      <Switch label="Normal" />
      <Switch label="Error" error />
      <Switch label="Disabled" disabled />
      <Switch label="Active" active />
    </Form>
  );
}

Press Enter to start editing.

Controlling Switches

The Switch can be controlled by providing the checked prop and onChange like a native <input type="checkbox">:

"use client";

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement, useState } from "react";

export default function ControllingSwitches(): ReactElement {
  const [checked, setChecked] = useState(false);
  return (
    <Form className={box()}>
      <Switch
        label="Label"
        checked={checked}
        onChange={(event) => {
          setChecked(event.currentTarget.checked);
        }}
      />
    </Form>
  );
}

Press Enter to start editing.

Switch with Messages

The Switch component is wrapped in the FormMessageContainer so additional hint or error messages can be displayed.

Help Text

Error!

Help Text

Error!

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement } from "react";

export default function SwitchWithMessages(): ReactElement {
  return (
    <Form className={box({ align: "start", stacked: true })}>
      <Switch label="Label" messageProps={{ children: "Help Text" }} />
      <Switch
        label="Label"
        error
        messageProps={{ children: "Error!", error: true }}
      />
      <Switch
        label="Stacked"
        stacked
        iconAfter
        messageProps={{ children: "Help Text" }}
      />
      <Switch
        label="Stacked"
        error
        stacked
        iconAfter
        messageProps={{ children: "Error!", error: true }}
      />
    </Form>
  );
}

Press Enter to start editing.

Switch with Circular Progress

The Switch can has been updated to support rendering a CircularProgress within the ball by using the ballAddon prop.

The ballAddon can also be used to render icons, but some additional styles will be required to update the color of the icon between the unchecked and checked states.

"use client";

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Switch } from "@react-md/core/form/Switch";
import { CircularProgress } from "@react-md/core/progress/CircularProgress";
import { useAsyncFunction } from "@react-md/core/useAsyncFunction";
import { randomInt } from "@react-md/core/utils/randomInt";
import { wait } from "@react-md/core/utils/wait";
import { cnb } from "cnbuilder";
import { type ReactElement, useId, useState } from "react";

import styles from "./SwitchWithCircularProgress.module.scss";

export default function SwitchWithCircularProgress(): ReactElement {
  const id = useId();
  const [checked, setChecked] = useState(false);
  const { handleAsync, pending } = useAsyncFunction();
  return (
    <Form className={box()}>
      <Switch
        id={id}
        label="Label"
        ballAddon={pending && <CircularProgress aria-labelledby={id} />}
        checked={checked}
        onChange={handleAsync(async (event) => {
          const nextChecked = event.currentTarget.checked;
          setChecked(nextChecked);
          await wait(4000);

          // randomly fail some async action
          if (randomInt() % 3 === 0) {
            setChecked(!nextChecked);
          }
        })}
        ballClassName={cnb(pending && styles.pending)}
      />
    </Form>
  );
}

Press Enter to start editing.

@use "everything";

.pending {
  // set the background color to the inactive color so that the circular
  // progress bar will be visible while checked
  @include everything.switch-set-var(
    ball-background-color,
    everything.$switch-ball-background-color
  );
}

Press Enter to start editing.