Toggle
A single-select toggle group that allows the user to choose one option from a set. Each option can display text or an icon.
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function ToggleDemo() {
return (
<Toggle
defaultValue="monthly"
options={[
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
);
}Toggle an accessible name by either pairing it with a <label> via htmlFor or giving it an aria-label. Without one, assistive technology announces the field with no indication of what it is for.Options
| Prop | Type | Description | Default |
|---|---|---|---|
options | ToggleOption[] | The list of toggle options. Each has value and either text or icon (with required ariaLabel). | — |
size | "sm" | "md" | "lg" | The size of the toggle and its options. | "md" |
value | string | The currently selected value (controlled mode). | — |
defaultValue | string | The initially selected value (uncontrolled mode). | — |
onValueChange | (value: string) => void | Callback fired when the selected value changes. | — |
disabled | boolean | When true, disables all toggle options. | false |
name | string | The name of the underlying select, used as the field name when submitted with form data. | — |
required | boolean | When true, makes the underlying input required when rendered inside of a form. | false |
Any other props are forwarded to the wrapping <div> element.
ToggleOption
| Prop | Type | Description | Default |
|---|---|---|---|
value | string | A unique value identifying this toggle option. | — |
text | string | The text label displayed in the toggle option. | — |
icon | ReactNode | An icon element displayed instead of text. | — |
ariaLabel | string | An accessible aria-label. Required for icon-only options. | — |
Examples
Controlled vs uncontrolled
Render a Toggle in an uncontrolled fashion to let it keep track of its own state. Pass defaultValue if it should start out with a value pre-selected:
import { Grid, List } from "@tenstorrent/vesper/icons";
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function UncontrolledToggle() {
return (
<Toggle
aria-label="Display options"
defaultValue="grid"
options={[
{ value: "grid", icon: <Grid />, ariaLabel: "Grid view" },
{ value: "list", icon: <List />, ariaLabel: "List view" },
]}
/>
);
}If you need to control the toggle's state, you can do so via the value and onValueChange props:
Value: nothing selected
import { useState } from "react";
import { Grid, List } from "@tenstorrent/vesper/icons";
import { Toggle } from "@tenstorrent/vesper/toggle";
import { Typography } from "@tenstorrent/vesper/typography";
export default function ControlledToggle() {
const [value, setValue] = useState("");
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Toggle
aria-label="Display options"
value={value}
onValueChange={setValue}
options={[
{ value: "list", icon: <List />, ariaLabel: "List view" },
{ value: "grid", icon: <Grid />, ariaLabel: "Grid view" },
]}
/>
<Typography variant="copy-sm">
Value: {value || "nothing selected"}
</Typography>
</div>
);
}Text and icon options
You can render both text and icon options in a Toggle. To render text options, you would pass an array of options objects that each have value and text properties:
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function TextOptionsToggle() {
return (
<Toggle
aria-label="Membership tier"
options={[
{ value: "free", text: "Free" },
{ value: "basic", text: "Basic" },
{ value: "pro", text: "Pro" },
]}
defaultValue="free"
/>
);
}To render icon options, pass an array of options objects that each have value, icon, and ariaLabel properties:
import { Globe, Lock } from "@tenstorrent/vesper/icons";
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function IconOptionsToggle() {
return (
<Toggle
aria-label="Project visibility"
options={[
{ value: "public", icon: <Globe />, ariaLabel: "Public" },
{ value: "private", icon: <Lock />, ariaLabel: "Private" },
]}
defaultValue="public"
/>
);
}Different sizes
Toggle can be rendered in one of three sizes; "sm", "md", or "lg". The default size is "md".
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function AllToggleSizes() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Toggle
size="sm"
defaultValue="monthly"
aria-label="Billing period"
options={[
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
<Toggle
size="md"
defaultValue="monthly"
aria-label="Billing period"
options={[
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
<Toggle
size="lg"
defaultValue="monthly"
aria-label="Billing period"
options={[
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
</div>
);
}Required fields
Passing required marks the toggle as required for native form validation:
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function RequiredToggleDemo() {
return (
<Toggle
required
name="billing_period"
aria-label="Billing period"
defaultValue="monthly"
options={[
{ value: "weekly", text: "Weekly" },
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
);
}Disabling the toggle
You can disable a Toggle by passing disabled={true} or just disabled as a prop. A disabled toggle still displays its current state, but it cannot be toggled by pointer or keyboard, is skipped in the tab order, and is excluded from form data:
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function DisabledToggle() {
return (
<Toggle
disabled
name="billing_period"
aria-label="Billing period"
defaultValue="monthly"
options={[
{ value: "weekly", text: "Weekly" },
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
);
}Usage in forms
Toggle renders a native <select>, so it works with regular form submission and native validation via props like name, value, form, and required.
Pass required when the toggle must have an option selected before the form can be submitted, such as a terms and conditions agreement:
import { useState } from "react";
import { Button } from "@tenstorrent/vesper/button";
import { Toggle } from "@tenstorrent/vesper/toggle";
import { Typography } from "@tenstorrent/vesper/typography";
export default function FormToggleDemo() {
const [submitted, setSubmitted] = useState<string>("");
return (
<form
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
console.log(Object.fromEntries(data.entries()));
setSubmitted(String(data.get("billing_period") ?? ""));
}}
>
<Toggle
name="billing_period"
aria-label="Billing period"
options={[
{ value: "weekly", text: "Weekly" },
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
<Button size="sm" type="submit">
Submit
</Button>
{!!submitted && (
<Typography variant="copy-sm">Submitted value: {submitted}</Typography>
)}
</form>
);
}The form above submits billing_period=monthly when the "Monthly" toggle option is selected, and refuses to submit at all while the toggle is empty.
Use the form prop to associate the field with a <form> rendered elsewhere on the page:
import { Button } from "@tenstorrent/vesper/button";
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function ToggleFormPropDemo() {
return (
<div>
<form id="signup" action="/api/signup" method="post">
<Button type="submit">Create account</Button>
</form>
{/* rendered outside of the form, but submitted with it */}
<Toggle
required
form="signup"
name="billing_period"
aria-label="Billing period"
options={[
{ value: "weekly", text: "Weekly" },
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
</div>
);
}Accessing the underlying element
Use the ref prop when you need direct access to the underlying <div> element:
import { useRef } from "react";
import { Toggle } from "@tenstorrent/vesper/toggle";
export default function ToggleRefDemo() {
const ref = useRef<HTMLDivElement>(null);
return (
<Toggle
ref={ref}
name="billing_period"
aria-label="Billing period"
options={[
{ value: "weekly", text: "Weekly" },
{ value: "monthly", text: "Monthly" },
{ value: "yearly", text: "Yearly" },
]}
/>
);
}