Toggle
A button-like component for toggling between on and off states, commonly used for formatting options like bold, italic, or other binary settings.
import { Toggle } from "~/components/toggle";
export default function ToggleBasicDemo() {
return (
<Toggle aria-label="Toggle bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
</svg>
</Toggle>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add toggleManual
Create the recipe file at src/components/recipes/toggle.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const toggleVariants = tv({
slots: {
root: "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium border border-input bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 transition duration-300 motion-safe:hover:-translate-y-0.5 motion-safe:active:translate-y-0.5 active:duration-150 data-[state=on]:bg-primary data-[state=on]:text-primary-foreground data-[state=on]:border-transparent data-[disabled]:pointer-events-none data-[disabled]:opacity-50 w-fit",
indicator: "inline-flex items-center justify-center",
},
variants: {
size: {
sm: { root: "h-8 px-2" },
md: { root: "h-9 px-2.5" },
lg: { root: "h-10 px-3" },
},
},
defaultVariants: {
size: "sm",
},
});
export type ToggleVariants = VariantProps<typeof toggleVariants>;Create the component directory and files.
src/components/toggle/toggle.base.tsx:
import { Toggle as ArkToggle } from "@ark-ui/solid/toggle";
import { splitProps, type Component } from "solid-js";
import { toggleVariants, type ToggleVariants } from "../recipes/toggle";
const styles = toggleVariants();
const Root: Component<ArkToggle.RootProps & ToggleVariants> = (props) => {
const [local, others] = splitProps(props, ["class", "size"]);
return (
<ArkToggle.Root class={styles.root({ class: local.class, size: local.size })} {...others} />
);
};
const Indicator: Component<ArkToggle.IndicatorProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkToggle.Indicator class={styles.indicator({ class: local.class })} {...others} />;
};
const Context = ArkToggle.Context;
export const Toggle = { Root, Indicator, Context };src/components/toggle/index.tsx:
import { Toggle as ToggleBase } from "./toggle.base";
const Toggle = ToggleBase.Root;
const ToggleIndicator = ToggleBase.Indicator;
export { Toggle, ToggleIndicator, ToggleBase };
export { toggleVariants, type ToggleVariants } from "../recipes/toggle";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 { Toggle } from "~/components/toggle";
export default function ToggleBasicDemo() {
return (
<Toggle aria-label="Toggle bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
</svg>
</Toggle>
);
}Default Pressed
Use the defaultPressed prop to set the initial pressed state.
import { Toggle } from "~/components/toggle";
export default function ToggleDefaultPressedDemo() {
return (
<Toggle defaultPressed aria-label="Toggle bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
</svg>
</Toggle>
);
}Controlled
Control the pressed state programmatically using pressed and onPressedChange.
import { createSignal } from "solid-js";
import { Toggle } from "~/components/toggle";
export default function ToggleControlledDemo() {
const [pressed, setPressed] = createSignal(false);
return (
<Toggle pressed={pressed()} onPressedChange={setPressed} aria-label="Toggle bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
</svg>
</Toggle>
);
}Disabled
Add the disabled prop to disable interaction.
import { Toggle } from "~/components/toggle";
export default function ToggleDisabledDemo() {
return (
<Toggle disabled aria-label="Toggle bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
<path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />
</svg>
</Toggle>
);
}With Indicator
ToggleIndicator renders different content based on the toggle state — the fallback is shown when off, and the children are shown when on.
import { Toggle, ToggleIndicator } from "~/components/toggle";
export default function ToggleIndicatorDemo() {
return (
<div class="flex items-center gap-4">
<Toggle>
<ToggleIndicator
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</svg>
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
</ToggleIndicator>
</Toggle>
<Toggle defaultPressed>
<ToggleIndicator
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="5" />
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</svg>
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
</ToggleIndicator>
</Toggle>
</div>
);
}Sizes
Use the size prop to change the toggle size.
import { Toggle } from "~/components/toggle";
export default function ToggleSizesDemo() {
return (
<div class="flex flex-wrap items-center gap-4">
<Toggle size="sm">Sm</Toggle>
<Toggle size="md">Md</Toggle>
<Toggle size="lg">Lg</Toggle>
</div>
);
}API Reference
See the Ark UI Toggle documentation.