nori-ui

Toggle

A two-state button — standalone for toolbar actions, or clustered as Toggle.Group for radiogroup-style choices.

4.0 kBgzipped

At a glance

  • A single bistable button — think of the B / I / U buttons in a rich-text toolbar.
  • Cluster several with <Toggle.Group> for shared selection semantics (toolbar-multiple, alignment-single).
  • ARIA: role="button" + aria-pressed per item. <Toggle.Group> adds role="group" (multiple) or role="radiogroup" (single).

Toggle vs Switch vs Checkbox vs SegmentedControl

These four controls look similar but ship different semantics — pick the one that matches the meaning of the choice, not the shape of the visual.

  • Toggle — a button with on/off state. Use it for toolbar-style actions (Bold, Pin, Mute). Lives outside <form>.
  • Toggle.Group (type="single") — a cluster where one or none can be active. Re-clicking the active item clears it. Use this when "no selection" is a valid state.
  • SegmentedControl — exactly one is always active. Use when "no selection" isn't a valid state.
  • Switch — a setting that takes effect immediately ("Dark mode", "Email notifications"). Reads as role="switch".
  • Checkbox — a value selected as part of a form. Reads as role="checkbox".

Standalone Toggle

Direction:

pressed and defaultPressed

Controlled mode: pass pressed and pair with onChange. Uncontrolled mode: pass defaultPressed and let the Toggle track state.

// Controlled
const [bold, setBold] = useState(false);
<Toggle pressed={bold} onChange={setBold}>B</Toggle>
 
// Uncontrolled
<Toggle defaultPressed>I</Toggle>

variant

Visual treatment of the toggle when off and on.

ValueOff stateOn state
default (default)Subtle border + bgTinted background + accent border
outline1px border, no fillTinted background + accent border
<Toggle variant="outline">Pin</Toggle>

size

Density. Aligned with Button's scale.

ValueMin height
sm32 px
md (default)36 px
lg44 px
<Toggle size="sm">B</Toggle>

disabled

Greys the toggle out and blocks press handling. Forwarded as aria-disabled.

<Toggle disabled>Locked</Toggle>

aria-label / accessibilityLabel

When the visible content is just an icon, pass aria-label (web) or accessibilityLabel (native) so screen readers say something meaningful.

<Toggle aria-label="Bold"><BoldIcon /></Toggle>

<Toggle.Group>

Clusters several Toggle items with shared selection semantics. Roving-tabindex keyboard nav follows the WAI-ARIA radiogroup pattern: arrow keys move focus, Home / End jump to the ends, Space / Enter toggles the focused item. Default-variant grouped items share borders so the cluster reads as one segmented chip — first item rounds the left, last rounds the right.

type — multiple vs single

The shape of value follows from type:

typevalue typeRe-click behavior
multiplestring[]Toggles the item in / out of the array
singlestring | undefinedRe-clicking the active item clears it

Multiple selection (formatting toolbar)

type="multiple"value is string[]. Clicking adds or removes an item from the array.

Direction:

Single selection (alignment)

type="single"value is string | undefined. Clicking sets it; clicking the active item again clears it.

Direction:

value and defaultValue

Controlled (value + onChange) vs uncontrolled (defaultValue). Their shapes follow type.

<Toggle.Group type="multiple" value={['bold', 'italic']} onChange={setMarks}>
    <Toggle.Item value="bold">B</Toggle.Item>
    <Toggle.Item value="italic">I</Toggle.Item>
</Toggle.Group>
 
<Toggle.Group type="single" defaultValue="left">
    <Toggle.Item value="left">L</Toggle.Item>
    <Toggle.Item value="center">C</Toggle.Item>
    <Toggle.Item value="right">R</Toggle.Item>
</Toggle.Group>

onChange

Fires with the next value whenever the user toggles an item. Pair with value for controlled, omit for uncontrolled.

<Toggle.Group type="multiple" onChange={(marks) => editor.setMarks(marks)}>
    {/* … */}
</Toggle.Group>

disabled

Disables every item in the group. Individual items can still set their own disabled.

<Toggle.Group disabled type="single" defaultValue="bold">{/* … */}</Toggle.Group>

variant and size (group-level)

Forwarded to every item by default — set the cluster's look once and items inherit. Per-item variant / size overrides the group value.

<Toggle.Group variant="outline" size="sm" type="single">{/* … */}</Toggle.Group>

aria-label / accessibilityLabel

The group's accessible name. Required for assistive tech to announce the cluster ("Text formatting, group"). For icon-only items inside, give each item its own aria-label too.

<Toggle.Group aria-label="Text formatting" type="multiple">
    <Toggle.Item value="bold" aria-label="Bold">B</Toggle.Item>
    <Toggle.Item value="italic" aria-label="Italic">I</Toggle.Item>
</Toggle.Group>

Props

<Toggle>

PropTypeDefaultDescription
accessibilityLabelstring
aria-labelstringRequired when `children` is icon-only.
childrenReactNodeVisible label or icon content.
classNamestring
defaultPressedbooleanfalseUncontrolled initial pressed state. Ignored when `pressed` is provided.
disabledbooleanfalseGroup-level disable (also forwarded by `<ToggleGroup>` to its items).
onChange(next: boolean) => voidFires with the next pressed state when the user toggles.
pressedbooleanControlled pressed state. Pair with `onChange`.
sizeenummd@defaultValue 'md'
testIDstring
variantenumdefaultVisual treatment. - `default` — transparent when off, filled with `interactive.primary` when on. - `outline` — bordered when off, tinted background + accent border when on. @defaultValue 'default'

<Toggle.Group>

PropTypeDefaultDescription
type*enumMulti-select gives an array; single-select gives a string (or undefined).
accessibilityLabelstring
aria-labelstring
classNamestring
defaultValuestring | string[]
disabledbooleanfalseGroup-level disable. Each item's `disabled` is OR-ed with this.
onChange((next: string) => void) | ((next: string[]) => void)
sizeenummd@defaultValue 'md'
testIDstring
valuestring | string[]
variantenumdefault@defaultValue 'default'

<Toggle.Item>

PropTypeDefaultDescription
value*stringUnique identifier within the group — written into `value` when pressed.
accessibilityLabelstring
aria-labelstring
classNamestring
disabledbooleanfalseDisable just this item (OR-ed with group-level `disabled`).
testIDstring
Preview theme