Meowland docs
Workbench

Component reference

SegmentedControl

A single choice, using native radio keyboard semantics; tabs remain a separate component.

Preview

Form ownership and reset

Two controlled groups sit inside and outside a form; native reset does not change the host's current value.

packages/meowtail/stories/selection-feedback.stories.tsxline 17tsx
render() {
  const options = [
    { value: "a", label: "A" },
    { value: "b", label: "B" },
  ];
  const inside = signal("a");
  const outside = signal("a");
  return (
    <Stack gap="relaxed" align="start">
      <form id="segmented-inside">
        <Stack gap="compact" align="start">
          <SegmentedControl
            id="inside-choice"
            name="mode"
            label="Inner choice"
            value={inside}
            options={options}
            onValueChange={(value) => {
              inside.value = value;
            }}
          />
          <Button type="reset" variant="outline" size="small">
            Reset inner form
          </Button>
        </Stack>
      </form>
      <Stack gap="compact" align="start">
        <SegmentedControl
          id="outside-choice"
          name="mode"
          form="segmented-outside"
          label="Outer choice"
          value={outside}
          options={options}
          onValueChange={(value) => {
            outside.value = value;
          }}
        />
        <form id="segmented-outside">
          <Button type="reset" variant="outline" size="small">
            Reset outer form
          </Button>
        </form>
      </Stack>
    </Stack>
  );
}

Installation

bun add meowtail

Usage

usage.tsts
import { SegmentedControl } from "meowtail";

Examples

The other stories that show this component.

Segmented control, active text and confirmation

The segmented control keeps native radio semantics. Confirmation is only used for this local example and introduces no global action gate.

packages/meowtail/stories/selection-feedback.stories.tsxline 73tsx
render() {
  const mode = signal("comfortable");
  const locked = signal(false);
  const active = signal(true);
  const open = signal(false);
  const acknowledged = signal(false);
  const feedback = signal("The example has not been removed.");
  return (
    <Stack align="start">
      <SegmentedControl
        id="reading-mode"
        label="Reading spacing"
        value={mode}
        disabled={locked}
        onValueChange={(next) => {
          mode.value = next;
        }}
        options={[
          { value: "comfortable", label: "Comfortable" },
          { value: "locked", label: "Automatic", disabled: true },
          { value: "compact", label: "Compact" },
        ]}
      />
      <Button
        variant="quiet"
        onClick={() => {
          locked.value = !locked.value;
        }}
      >
        Toggle selection lock
      </Button>
      <TextShimmer text="Reading the file content" active={active} />
      <TextShimmer active={active} className="compound-shimmer">
        <TextShimmer text="Checking" />
        {" · "}
        <TextShimmer text="src/components" />
      </TextShimmer>
      <Button
        variant="quiet"
        onClick={() => {
          active.value = !active.value;
        }}
      >
        Toggle active state
      </Button>
      <Button
        variant="outline"
        onClick={() => {
          acknowledged.value = false;
          open.value = true;
        }}
      >
        Remove example
      </Button>
      <RiskConfirmation
        id="remove-sample"
        open={open}
        title="Remove example"
        description="This is a local component example; it does not delete real files."
        acknowledgeLabel="I understand this action only affects the example."
        cancelLabel="Cancel"
        confirmLabel="Confirm removal"
        acknowledged={acknowledged}
        onAcknowledgedChange={(next) => {
          acknowledged.value = next;
        }}
        onCancel={() => {
          open.value = false;
        }}
        onConfirm={() => {
          open.value = false;
          feedback.value = "Removed the local example.";
        }}
      />
      <Note role="status">{feedback}</Note>
    </Stack>
  );
}

API reference

Props

PropTypeDefault
idRequired
string–
name
stringid
form
string–
labelRequired
string–
valueRequired
MaybeSignal<string>–
optionsRequired
readonly SegmentedControlOption[]–
onValueChangeRequired
(value: string) => void–
disabled
MaybeSignal<boolean>–

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

DOM parts

rootlegendoptionsoptioninputlabel

Registry

The component is registered as segmented-control in the meowtail component registry.