Switch
An on/off control that takes effect the moment it is flipped.
import { Div, Label } from "@implementjs/core";
import { Switch } from "@/lib/components/ui/switch";
export default function SwitchDemo() {
return Div(
{ class: "flex flex-col gap-4" },
Div(
{ class: "flex items-center gap-2" },
Switch({ id: "airplane" }),
Label({ for: "airplane", class: "text-sm leading-none font-medium" }, "Airplane mode"),
),
Div(
{ class: "flex items-center gap-2" },
Switch({ id: "marketing", checked: true }),
Label({ for: "marketing", class: "text-sm leading-none font-medium" }, "Marketing emails"),
),
Div(
{ class: "flex items-center gap-2" },
Switch({ id: "disabled-switch", checked: true, disabled: true }),
Label(
{
for: "disabled-switch",
class:
"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
},
"Disabled",
),
),
);
}Installation
npx jsrepo add @implementjs/ui/switch
Nothing else comes with it — this one stands alone on @implementjs/core and @implementjs/primitives.
Copy the file below to src/lib/components/ui/switch.ts. It imports cn, so copy utils.ts to src/lib/utils.ts too.
import { type Child, type ComponentProps } from "@implementjs/core";
import {
Switch as SwitchPrimitive,
SwitchThumb as SwitchThumbPrimitive,
} from "@implementjs/primitives";
import { createComponent } from "@implementjs/primitives";
import { cn } from "@/lib/utils";
export type SwitchProps = ComponentProps<typeof SwitchPrimitive>;
export type SwitchThumbProps = ComponentProps<typeof SwitchThumbPrimitive>;
export const Switch = createComponent(function Switch(
{ class: className, ...props }: SwitchProps,
...children: Child[]
) {
return SwitchPrimitive(
{
...props,
"data-slot": "switch",
class: cn(
"peer group/switch inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none transition-all",
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
"dark:data-[state=unchecked]:bg-input/80",
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"disabled:cursor-not-allowed disabled:opacity-50",
className,
),
},
...(children.length > 0 ? children : [SwitchThumb({})]),
);
});
export const SwitchThumb = createComponent(function SwitchThumb(
{ class: className, ...props }: SwitchThumbProps,
...children: Child[]
) {
return SwitchThumbPrimitive(
{
...props,
"data-slot": "switch-thumb",
class: cn(
"pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform",
"data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
"dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground",
className,
),
},
...children,
);
});Usage
A switch is for a setting that applies immediately. When the change only lands on submit, use a checkbox.
The root renders a SwitchThumb for you unless you pass children — the thumb is exported for when you want a different one.
import { signal } from "@implementjs/core";
import { Switch } from "@/lib/components/ui/switch";
const airplaneMode = signal(false);
Switch({ id: "airplane", checked: airplaneMode });
With a label
Div(
{ class: "flex items-center gap-2" },
Switch({ id: "airplane" }),
Label({ for: "airplane", class: "text-sm font-medium" }, "Airplane mode"),
);
API Reference
Every prop the styling does not consume is forwarded to the Switch primitive, so the tables below are the whole surface — the behavior props and the styling ones together.
Switch
A toggle that is on or off. Sets role="switch" and aria-checked. Give it a track and a thumb; it handles the state. Renders a SwitchThumb for you unless you pass children of your own. Renders a Button; extra props are forwarded onto it.
| Prop | Type | Default | Description |
|---|---|---|---|
checked | Signal<boolean> | boolean | false | The checked state. Pass a signal to control it from outside; a boolean seeds uncontrolled state. |
name | string | — | If set, a hidden checkbox is rendered so the value submits with a form. |
value | string | "on" | The value submitted while checked. Only used when name is set. |
required | boolean | false | Marks the hidden input as required. Sets aria-required on the button. |
| Data attribute | Value |
|---|---|
[data-switch-root] | Present |
[data-state] | "checked" | "unchecked" |
SwitchThumb
The knob. Put it inside the switch and slide it with data-state. Styled as a circle that slides across when the switch turns on. Renders a Span; extra props are forwarded onto it.
| Data attribute | Value |
|---|---|
[data-switch-thumb] | Present |
[data-state] | "checked" | "unchecked" |