Propel

Switch

Toggles a single setting on or off.

Show code
import { Switch } from "@makeplane/propel/components/switch";
import * as React from "react";

export default function BasicDemo() {
  const [checked, setChecked] = React.useState(true);

  return (
    <Switch
      size="md"
      aria-label="Enable notifications"
      checked={checked}
      onCheckedChange={setChecked}
    />
  );
}

Installation

import { Switch } from "@makeplane/propel/components/switch";

Usage

import { Switch } from "@makeplane/propel/components/switch";
import * as React from "react";

export default function BasicDemo() {
  const [checked, setChecked] = React.useState(true);

  return (
    <Switch
      size="md"
      aria-label="Enable notifications"
      checked={checked}
      onCheckedChange={setChecked}
    />
  );
}

Examples

Sizes

size sets the track, in the order Figma lists it: lg 30×18, md 27×16, sm 23×14 px. The thumb derives from the track and is not its own axis. Every track is under WCAG 2.5.8’s 24px floor, so each size carries a transparent ring that brings the pointer target up to it.

Show code
import { Switch, type SwitchSize } from "@makeplane/propel/components/switch";

const SIZES: SwitchSize[] = ["lg", "md", "sm"];

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {SIZES.map((size) => (
        <Switch key={size} size={size} defaultChecked aria-label={`Size ${size}`} />
      ))}
    </div>
  );
}

States

Switches read as off, on, disabled, or read-only. A disabled on switch dims to 40% opacity; a disabled off switch swaps to a solid muted track instead. Read-only dims either state without blocking the pointer.

Show code
import { Switch } from "@makeplane/propel/components/switch";

export default function StatesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Switch size="md" defaultChecked aria-label="On" />
      <Switch size="md" defaultChecked={false} aria-label="Off" />
      <Switch size="md" defaultChecked disabled aria-label="Disabled on" />
      <Switch size="md" defaultChecked={false} disabled aria-label="Disabled off" />
      <Switch size="md" defaultChecked readOnly aria-label="Read only on" />
      <Switch size="md" defaultChecked={false} readOnly aria-label="Read only off" />
    </div>
  );
}

With description

Automatically restart the service after a crash.

Show code
import { SwitchField } from "@makeplane/propel/components/switch-field";

export default function WithDescriptionDemo() {
  return (
    <SwitchField
      name="restartOnFailure"
      label="Restart on failure"
      description="Automatically restart the service after a crash."
      switchSize="md"
      size="lg"
      defaultChecked
    />
  );
}

Native button

The switch renders as a <span> by default, so the simplest way to label it is wrapping it in a <label> element. When the label needs to be a sibling instead — wired up with htmlFor/id rather than a wrapper — pass nativeButton and a host element via render (typically <button type="button" />). The styled track (and its size) stay on Switch; only the underlying element swaps. The look is unchanged, and clicking the sibling label still toggles the switch.

Show code
import { Switch } from "@makeplane/propel/components/switch";

export default function NativeButtonDemo() {
  return (
    <div className="flex items-center gap-2">
      <Switch
        size="md"
        defaultChecked
        id="notifications-switch"
        nativeButton
        render={<button type="button" />}
      />
      <label htmlFor="notifications-switch" className="text-body-xs-regular text-secondary">
        Notifications
      </label>
    </div>
  );
}

API Reference

Switch

The ready-made switch: grafts Base UI's `Switch` behavior onto the styled `Switch` track and `SwitchThumb` knob. The track owns the required `size`; the thumb sizes itself from it. Base UI supplies `role="switch"` and full keyboard/form support. Maps to Figma's "Toggle" component. On/off, `disabled`, and `readOnly` are control state from the primitive, not variants — pass them as props (`checked`/`defaultChecked`, `disabled`, `readOnly`). Only the size axis (`size`) is a visual variant. When `nativeButton` is set, pass a host element via `render` (typically `<button type="button" />`) — `size` still lives on this component and is applied to the styled track that wraps the host, so callers do not re-specify size on `SwitchTrack`.

PropTypeDefaultDescription
size(required)"lg" | "md" | "sm"The switch size axis.
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SwitchRootState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.