Toggle
Two-state toggle button.
Usage
Toggle is a two-state button. Bits renders a real <button> and owns aria-pressed, so the
state is announced without any work on your side. An icon-only toggle carries no text, so it
needs an aria-label — otherwise it has no accessible name at all.
Variants
outline keeps a visible border when off; ghost shows nothing until hovered or pressed. Both use the primary tone
for the on state.
Sizes
Three sizes matched to the Button scale, so they line up in a toolbar.
Toggle vs Switch
They are not interchangeable, and the difference is about when the change takes effect. A Switch is a setting — flipping it applies immediately, like
turning on notifications. A Toggle is a button that stays pressed —
bold in a text editor, a filter that is active. If the control belongs in a toolbar next to
other buttons, you want Toggle. If it belongs in a settings form, you
want Switch.
Need several related toggles where only one can be active? That is Toggle Group, not a row of Toggles.
Props
Plus every native <button> attribute via prop spreading. pressed is bindable.
| Prop | Type | Default |
|---|---|---|
pressed Whether the toggle is pressed. Bindable. | boolean | false |
disabled | boolean | false |
size | 'sm' | 'md' | 'lg' | 'md' |
variant | 'outline' | 'ghost' | 'outline' |
invalid Marks the control as failing validation. Always applies the invalid styling. Does NOT set `aria-invalid`: this renders as `button`, and ARIA does not support the attribute there, so assistive technology is free to ignore it. axe does NOT flag it either way — verified by injecting it and watching the suite still pass — so this is a decision taken from the spec, not one a tool enforces. The accessible signal comes from `Field` wiring the error message through `aria-describedby`. Prefer letting `Field` drive this: passing `Field` an `error` is what makes a field invalid, so the message the user reads and the state of the control cannot disagree. | boolean | false |
required Accepted and deliberately NOT forwarded. This renders as a `<button>`. A native `required` attribute is inert there, and `aria-required` is worse — axe reports it as an `aria-allowed-attr` violation, verified rather than assumed. (Note `aria-invalid` differs: axe accepts that one on a button. The two rules are not the same.) So it is swallowed here rather than emitted as dead markup. When wrapped by `Field`, the required signal a user gets is the marker on the visible label plus the error on submit — which is what `Field` is for. | boolean | false |
class | string | — |
child bits-ui | Snippet<[ToggleRootSnippetProps & { props: Record<string, unknown>; }]> | undefined | — |
children bits-ui | Snippet<[ToggleRootSnippetProps]> | undefined | — |
id bits-ui | string | undefined | — |
onPressedChange bits-ui A callback function called when the toggle is pressed. | OnChangeFn<boolean> | undefined | — |
ref bits-ui | HTMLElement | null | undefined | — |