Switch

A toggle switch input with an optional label. Uses the native switch ARIA role for binary on/off states, with no indeterminate state. Supports controlled and uncontrolled modes.

import { Switch } from "@tenstorrent/vesper/switch";

export default function SwitchDemo() {
  return <Switch label="Enable notifications" />;
}

Options

PropTypeDescriptionDefault
labelstringThe text label displayed next to the switch. An asterisk is appended when required is true.—
size"sm" | "md"The size of the switch and its label."md"
refRef<HTMLInputElement>A ref forwarded to the underlying <input> element.—
checkedbooleanControls the checked state (controlled mode).—
defaultCheckedbooleanThe initial checked state (uncontrolled mode).false
disabledbooleanWhen true, prevents interaction.false
requiredbooleanWhen true, marks the input as required and appends an asterisk to the label.false
onChangeChangeEventHandler<HTMLInputElement>Callback fired when the switch is toggled.—

Form and accessibility props such as autoFocus, tabIndex, aria-label, aria-labelledby, aria-describedby, aria-invalid, and event handlers like onFocus, onBlur, and onKeyDown are forwarded to the underlying <input> element, as is ref. Any other props, including className, are forwarded to the wrapping <label> element.

Use the Switch component when toggling a setting on and off takes effect immediately. Reach for the Checkbox component instead when building form fields, such as accepting terms or opting into a mailing list, or when you need to represent an indeterminate state.

Examples

Controlled vs uncontrolled

Render a Switch in an uncontrolled fashion to let it keep track of its own checked state. Pass defaultChecked if it should start out checked:

import { Switch } from "@tenstorrent/vesper/switch";

export default function UncontrolledSwitch() {
  return <Switch label="Allow notifications" defaultChecked />;
}

If you need to control whether the switch is checked, you can do so via the checked and onChange props. The new state is read from the event's target.checked:

Checked: false

import { useState } from "react";

import { Button } from "@tenstorrent/vesper/button";
import { Switch } from "@tenstorrent/vesper/switch";
import { Typography } from "@tenstorrent/vesper/typography";

export default function ControlledSwitch() {
  const [notificationsEnabled, setNotificationsEnabled] = useState(false);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--vesper-spacing-4)",
      }}
    >
      <Switch
        label="Allow notifications"
        checked={notificationsEnabled}
        onChange={(e) => setNotificationsEnabled(e.target.checked)}
      />
      <Typography variant="copy-sm">
        Checked: {String(notificationsEnabled)}
      </Typography>
      <Button size="sm" onClick={() => setNotificationsEnabled(false)}>
        Reset
      </Button>
    </div>
  );
}

Different sizes

The Switch component can be rendered at sm or md size, defaulting to md. Size affects the dimensions of the switch itself, as well as the text styles of the text rendered beside it.

import { Switch } from "@tenstorrent/vesper/switch";

export default function SwitchSizes() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--vesper-spacing-4)",
      }}
    >
      <Switch size="sm" label="A small switch" />
      <Switch size="md" label="A medium switch" />
    </div>
  );
}

Disabling the switch

You can disable a Switch by passing disabled={true} or just disabled as a prop. A disabled switch still displays its current state, but it cannot be toggled by pointer or keyboard, is skipped in the tab order, and is excluded from form data:

import { Switch } from "@tenstorrent/vesper/switch";

export default function DisabledSwitches() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--vesper-spacing-4)",
      }}
    >
      <Switch label="Sign up for our newsletter" disabled />
      <Switch label="Sign up for our newsletter" defaultChecked disabled />
    </div>
  );
}

Usage in forms

Switch renders a native <input type="checkbox">, so it works with regular form submission and native validation via props like name, value, form, and required. A switch that is turned on submits its value (defaulting to "on"), and a turned-off switch is omitted from form data entirely.

Pass required when the switch must be turned on before the form can be submitted:

import { useState } from "react";

import { Button } from "@tenstorrent/vesper/button";
import { Switch } from "@tenstorrent/vesper/switch";
import { Typography } from "@tenstorrent/vesper/typography";

export default function FormSwitchDemo() {
  const [submitted, setSubmitted] = useState<string | null>(null);

  return (
    <form
      style={{
        display: "flex",
        flexDirection: "column",
        gap: "var(--vesper-spacing-4)",
      }}
      onSubmit={(event) => {
        event.preventDefault();
        const data = new FormData(event.currentTarget);
        setSubmitted(String(data.get("notifications") ?? ""));
      }}
    >
      <Switch
        required
        name="notifications"
        value="enabled"
        label="Enable notifications"
      />
      <Button size="sm" type="submit">
        Submit
      </Button>
      {submitted !== null && (
        <Typography variant="copy-sm">
          Submitted value: notifications={submitted}
        </Typography>
      )}
    </form>
  );
}

The form above submits notifications=enabled once the switch is turned on, and refuses to submit at all while it is turned off.

Use the form prop to associate the field with a <form> rendered elsewhere on the page:

import { Button } from "@tenstorrent/vesper/button";
import { Switch } from "@tenstorrent/vesper/switch";

export default function SwitchFormPropDemo() {
  return (
    <div>
      <form id="settings" action="/api/settings" method="post">
        <Button type="submit">Save settings</Button>
      </form>
      {/* rendered outside of the form, but submitted with it */}
      <Switch
        required
        form="settings"
        name="notifications"
        value="enabled"
        label="Enable notifications"
      />
    </div>
  );
}

Accessing the underlying element

Use the ref prop when you need direct access to the underlying <input> element, eg. to focus it or report a custom validation message:

import { useRef } from "react";

import { Switch } from "@tenstorrent/vesper/switch";

export default function SwitchRefDemo() {
  const ref = useRef<HTMLInputElement>(null);

  return (
    <Switch
      required
      ref={ref}
      label="Enable telemetry"
      onChange={() => ref.current?.setCustomValidity("")}
      onInvalid={() =>
        ref.current?.setCustomValidity(
          "Telemetry must be enabled when opting into nightly builds",
        )
      }
    />
  );
}