Switch
A toggle switch component for on/off boolean input.
import { Switch, SwitchLabel } from "~/components/switch";
export default function SwitchBasicDemo() {
return (
<Switch defaultChecked>
<SwitchLabel>Enable notifications</SwitchLabel>
</Switch>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add switchManual
Create the recipe file at src/components/recipes/switch.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const switchVariants = tv({
slots: {
root: "inline-flex items-center gap-2 data-disabled:opacity-50 data-disabled:cursor-not-allowed",
control:
"inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
thumb:
"pointer-events-none block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
},
});
export type SwitchVariants = VariantProps<typeof switchVariants>;Create the component directory and files.
src/components/switch/switch.base.tsx:
import { Switch as ArkSwitch } from "@ark-ui/solid/switch";
import { splitProps, type Component } from "solid-js";
import { switchVariants, labelVariants } from "../recipes/switch";
const styles = switchVariants();
const SwitchRoot: Component<ArkSwitch.RootProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSwitch.Root class={styles.root({ class: local.class })} {...others} />;
};
const SwitchRootProvider: Component<ArkSwitch.RootProviderProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSwitch.RootProvider class={styles.root({ class: local.class })} {...others} />;
};
const SwitchControl: Component<ArkSwitch.ControlProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSwitch.Control class={styles.control({ class: local.class })} {...others} />;
};
const SwitchThumb: Component<ArkSwitch.ThumbProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSwitch.Thumb class={styles.thumb({ class: local.class })} {...others} />;
};
const SwitchLabel: Component<ArkSwitch.LabelProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkSwitch.Label class={labelVariants({ class: local.class })} {...others} />;
};
const SwitchHiddenInput = ArkSwitch.HiddenInput;
export const Switch = {
Root: SwitchRoot,
RootProvider: SwitchRootProvider,
Control: SwitchControl,
Thumb: SwitchThumb,
Label: SwitchLabel,
HiddenInput: SwitchHiddenInput,
};src/components/switch/index.tsx:
import { Switch as ArkSwitch } from "@ark-ui/solid/switch";
import { splitProps, type Component } from "solid-js";
import { Switch as SwitchBase } from "./switch.base";
import { labelVariants } from "../recipes/switch";
const SwitchControl = () => (
<>
<SwitchBase.Control>
<SwitchBase.Thumb />
</SwitchBase.Control>
<SwitchBase.HiddenInput />
</>
);
const SwitchLabel: Component<ArkSwitch.LabelProps> = (props) => (
<SwitchBase.Label class={labelVariants({ class: props.class })} {...props} />
);
const Switch: Component<ArkSwitch.RootProps> = (props) => {
const [local, others] = splitProps(props, ["class", "children"]);
return (
<SwitchBase.Root class={local.class} {...others}>
<SwitchControl />
{local.children}
</SwitchBase.Root>
);
};
const SwitchRootProvider: Component<ArkSwitch.RootProviderProps> = (props) => {
const [local, others] = splitProps(props, ["class", "children"]);
return (
<SwitchBase.RootProvider class={local.class} {...others}>
<SwitchControl />
{local.children}
</SwitchBase.RootProvider>
);
};
export { Switch, SwitchRootProvider, SwitchLabel, SwitchBase };
export { switchVariants, type SwitchVariants } from "../recipes/switch";Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Basic Usage
import { Switch, SwitchLabel } from "~/components/switch";
export default function SwitchBasicDemo() {
return (
<Switch defaultChecked>
<SwitchLabel>Enable notifications</SwitchLabel>
</Switch>
);
}Disabled
Add the disabled prop to disable interaction.
import { Switch, SwitchLabel } from "~/components/switch";
export default function SwitchDisabledDemo() {
return (
<Switch disabled>
<SwitchLabel>Enable notifications</SwitchLabel>
</Switch>
);
}Advanced Usage
Root Provider
Use SwitchRootProvider when you need to access the switch state outside of the switch tree. This pattern uses the useSwitch hook from Ark UI to create a shared context that both the switch and external elements can reference.
import { useSwitch } from "@ark-ui/solid/switch";
import { SwitchRootProvider, SwitchLabel } from "~/components/switch";
export default function SwitchRootProviderDemo() {
const sw = useSwitch({ defaultChecked: true });
return (
<div class="space-y-4">
<output class="block text-sm text-muted-foreground">
Checked: {JSON.stringify(sw().checked)}
</output>
<SwitchRootProvider value={sw}>
<SwitchLabel>Enable notifications</SwitchLabel>
</SwitchRootProvider>
</div>
);
}The key difference:
Switch(Root) — manages its own state internally. Use for simple, self-contained switches.SwitchRootProvider— accepts a pre-created switch context viauseSwitch. Use when you need to read or control the switch state from outside the component tree.
API Reference
See the Ark UI Switch documentation.