Toggle
Switches a setting on or off, applying the change immediately.
Installation
- Install the package.
npm install @hashintel/ds-components- Import the component.
import { useState } from "react";
import { Toggle } from "@hashintel/ds-components";
export const PublicAccessSetting = () => {
const [publicAccess, setPublicAccess] = useState(false);
return (
<Toggle
labelOnText="Public access"
value={publicAccess}
onChange={setPublicAccess}
/>
);
};When to use
- Do: Use Toggle for a binary setting that takes effect the moment it's flipped — enabling notifications, pausing a sync, making a workspace public.
- Do: Use it in settings panels and option lists where each row is an independent on/off.
- Do: Use it only where flipping back fully undoes the change — a toggle never asks for confirmation.
When not to use
- Don't: Don't use Toggle for a choice that only applies when the form is saved — use Checkbox, which reads as deferred.
- Don't: Don't use it to pick between two named alternatives, such as table and graph views — use SegmentedControl.
- Don't: Don't use it to run an action or anything needing confirmation — use Button, with a confirming Dialog for irreversible changes.
Examples
Basic
Toggle is always controlled: value holds the on/off boolean and onChange receives the next one (see Installation for the state wiring). Apply the change as soon as onChange fires — a toggle promises immediate effect.
Disabled
A disabled toggle keeps its state visible but can't be flipped. Explain the constraint nearby — the dimmed control alone doesn't say why it's locked.
Invalid
Set invalid when validation rejects the current state — it draws a red ring and sets aria-invalid. Pair it with visible error text that says what's wrong.
Tones
tone colors the track when the toggle is on: neutral (default) for everyday settings, brand for primary features, success when on means healthy or complete.
Off tone
offTone colors the track when the toggle is off. The default neutral reads as a plain "off"; error flags an off state that needs attention, such as a protection left disabled. Don't signal it with color alone — say why nearby.
Sizes
md is the default; the label text scales with the control. Use xxs–sm in dense chrome such as table rows and panel headers, lg for prominent settings.
Labels
labelOnText renders after the control, on the on side; labelOffText renders before it, on the off side. Use either one as the setting's label, or both to caption the two poles — both stay visible whichever way the switch sits.
In forms
Inside Form.Field, leave the label props off — the field's label is wired to the hidden input automatically. When on, the hidden native checkbox submits "on" under name; required blocks native submission while off.
import { useState } from "react";
import { Form, Toggle } from "@hashintel/ds-components";
export const InferenceSettingRow = () => {
const [aiInference, setAiInference] = useState(true);
return (
<Form.Field layout="inline" label="Enable AI inference">
<Toggle
name="aiInference"
value={aiInference}
onChange={setAiInference}
/>
</Form.Field>
);
};Behavior
- The whole component renders as one
<label>wrapping a hidden native checkbox, so clicking the track or either label toggles. Don't nest it inside another label or interactive element.
Content
- Label the setting, not the state: "Public access", not "On" or "Enabled". Keep the wording identical whichever way the switch sits.
- Use a short verb or noun phrase — 1–4 words, sentence case, no punctuation: "Autosave", "Sync entity changes".
- In general phrase for the on state: "Show archived entities", rather than "Hide archived entities". Switching off a negative is a double negative.
- Reserve the two-label form for true poles ("Off"/"On"); two named alternatives belong in SegmentedControl.
Accessibility
Toggle implements the WAI-ARIA switch pattern.
What Toggle provides
- A visually hidden native
<input type="checkbox">inside a<label>. labelOnText/labelOffTextrender as label parts that name the input.
What you must do
- Give every toggle a name:
labelOnText/labelOffText, a wrappingForm.Fieldlabel, oraria-labelwhen the visible label lives elsewhere. - Don't rely on
offTone="error"color alone — pair it with text that explains the problem. - Prefer explaining a constraint over
disabled— a disabled toggle has reduced contrast and doesn't say why it's locked.
API reference
| Prop | Type | Description |
|---|---|---|
| value | boolean | The input value |
| onChange | (value: boolean) => void | The onChange handler |
| tone | "neutral" | "brand" | "success" default: "neutral" | The tone applied when the toggle is on (checked) |
| offTone | "neutral" | "error" default: "neutral" | The tone applied when the toggle is off (unchecked) |
| labelOnText | string | Label rendered after the control, on the on side — visible in both states. |
| labelOffText | string | Label rendered before the control, on the off side — visible in both states. |
| name | string | The name of the input |
| onFocus | FocusEventHandler<HTMLInputElement> | The onFocus handler |
| onBlur | FocusEventHandler<HTMLInputElement> | The onBlur handler |
| invalid | boolean | Set to show the input as invalid |
| autoFocus | boolean | "never" | Set to true to make the element focused on mount or 'never' to prevent the item being auto-focused |
| disabled | boolean | Set to show the input as disabled |
| required | boolean | Marks the input as required: sets the native required attribute, so assistive technology announces it and form validation blocks empty submits. Purely functional — the input looks unchanged; pair it with Form.Field's required for the visible mark. |
| size | "xxs" | "xs" | "sm" | "md" | "lg" default: "md" | The size (height) of the element |
Was this page helpful?