Meowland docs
Workbench

Component reference

Switch

A native checkbox with switch semantics; form reset and keyboard remain native.

Preview

Switch has no story of its own; this one draws it as part of Checkbox.

Checkboxes and switches

Activate labels, toggle with the space bar and reset the form; a controlled switch leaves it to the caller to accept a change.

packages/meowtail/stories/toggle.stories.tsxline 13tsx
render: () => {
  const checked = signal(true);
  return (
    <form onSubmit={(event: Event) => event.preventDefault()}>
      <Stack>
        <Checkbox label="Save this choice" name="remember" defaultChecked />
        <Checkbox label="Required before submitting" name="confirm" required />
        <Checkbox label="Currently unavailable" disabled />
        <Checkbox label="Some children selected" indeterminate defaultChecked={false} />
        <Switch label="Show tool process" name="tools" defaultChecked />
        <Switch
          label="Notifications (controlled)"
          name="notifications"
          checked={checked}
          onChange={(event: Event) => {
            checked.value = (event.currentTarget as HTMLInputElement).checked;
          }}
        />
        <Switch label="Always on (rejects changes)" checked={true} />
        <Switch label="Unavailable" disabled />
        <ActionRow>
          <Button type="reset" variant="outline">
            Reset
          </Button>
          <Button type="submit">Confirm</Button>
        </ActionRow>
      </Stack>
    </form>
  );
}

Installation

bun add meowtail

Usage

Taken from the examples on this page.

usage.tsxtsx
import { Switch } from "meowtail";

<Switch label="Show tool process" name="tools" defaultChecked />

Examples

Other stories that draw Switch.

Control centers and symmetry

Checks the vertical center and top/bottom symmetry of single-line inputs, selects, button icons/text, and the inline composer on one screen; shared by visual review and geometry regressions.

packages/meowtail/stories/basic-quality.stories.tsxline 42tsx
render() {
  const choice = signal("current");
  const rowChoice = signal("current");
  const view = signal("list");
  return (
    <div className="workshop-stack" data-basic-quality-scene>
      <Specimen label="Single-line inputs and selects" align="start">
        <Input aria-label="Project name" value="Workspace" data-batch1="input" />
        <Input
          aria-label="Project name placeholder"
          placeholder="Project name"
          data-batch1="input-placeholder"
        />
        <Select aria-label="Current workspace" value="current" data-batch1="select">
          <option value="current">Current workspace</option>
          <option value="new">New workspace</option>
        </Select>
        <ChoiceSelect
          id="basic-quality-choice"
          aria-label="Select model"
          value={choice}
          onValueChange={(value) => {
            choice.value = value;
          }}
          data-batch1="choice-select"
          options={[
            { value: "current", label: "DeepSeek-V4-Pro" },
            { value: "fast", label: "DeepSeek-V4-Fast" },
          ]}
        />
      </Specimen>

      <Specimen label="One row of every single-line control" align="start">
        {/* Top-aligned on purpose: the row only lines up if every height agrees. */}
        <Inline gap="compact" align="start" data-mixed-row>
          <Input aria-label="Filter name" placeholder="Name" />
          <SearchInput label="Search files" placeholder="Search" />
          <Select aria-label="Status">
            <option>Any status</option>
          </Select>
          <ChoiceSelect
            id="basic-quality-row-choice"
            aria-label="Owner"
            value={rowChoice}
            onValueChange={(value) => {
              rowChoice.value = value;
            }}
            options={[
              { value: "current", label: "Anyone" },
              { value: "me", label: "Me" },
            ]}
          />
          <NumberInput defaultValue={10} aria-label="Limit" />
          <SegmentedControl
            id="basic-quality-row-view"
            label="View"
            value={view}
            onValueChange={(value) => {
              view.value = value;
            }}
            options={[
              { value: "list", label: "List" },
              { value: "grid", label: "Grid" },
            ]}
          />
          <Button variant="outline">Reset</Button>
          <Button>Apply</Button>
          <IconButton label="Refresh">
            <Icon name="refresh-cw" />
          </IconButton>
        </Inline>
      </Specimen>

      <Specimen label="Actions" align="start">
        <div className="workshop-row">
          <Button data-batch1="button-text">Save changes</Button>
          <Button data-batch1="button-icon-text">
            <Icon name="search" />
            Search
          </Button>
          <IconButton label="Search" data-batch1="icon-button">
            <Icon name="search" />
          </IconButton>
        </div>
      </Specimen>

      <Specimen label="Switches and checkboxes" align="start">
        <div className="workshop-row">
          <Checkbox label="Keep draft" defaultChecked data-batch1="checkbox" />
          <Switch label="Notify when complete" data-batch1="switch" />
        </div>
      </Specimen>

      <Specimen label="Inline input" align="start">
        <div style={{ width: "100%" }}>
          <ChatComposer
            layout="inline"
            placeholder="Write a message…"
            inputLabel="Message"
            submitLabel="Send"
            onSubmit={(event) => event.preventDefault()}
            data-batch1="composer"
          />
        </div>
      </Specimen>
    </div>
  );
}

API reference

Props

PropTypeDefault
checked
MaybeSignal<boolean>–
defaultChecked
boolean–
indeterminateNative indeterminate property. Assistive technology reads aria-checked="mixed".
MaybeSignal<boolean>–
onChange
(event: Event) => void–
labelRequired
MaybeSignal<string>–
labelHiddenKeep the accessible name while hiding the visible label, for compact tables.
boolean–

Also takes className, children and the attributes and events of its root element.

DOM parts

rootinputtrackthumblabel

Registry

The component is registered as switch in the meowtail component registry.