Askr documentation
UI & Components

Toggle Family

Toggle Family: anatomy, keyboard behavior, state, and theming in Askr.

Example

import { Toggle, ToggleGroup, ToggleGroupItem } from '@askrjs/themes/components';

<>
  <Toggle pressed={bold()} onPress={() => setBold((value) => !value)}>Bold</Toggle>
  <ToggleGroup type="single" value={alignment()} onValueChange={setAlignment}>
    <ToggleGroupItem value="left">Left</ToggleGroupItem>
    <ToggleGroupItem value="center">Center</ToggleGroupItem>
  </ToggleGroup>
</>

Published props

Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.

ToggleAsChildProps

Import from @askrjs/ui/toggle.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
disableddisabled?: boolean | undefined;
onPressonPress?: ((e: PressEvent) => void) | undefined;
pressedpressed?: boolean | undefined;
refref?: Ref<Element>;
typetype?: undefined;

ToggleButtonProps

Import from @askrjs/ui/toggle.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((e: PressEvent) => void) | undefined;
pressedpressed?: boolean | undefined;
refref?: Ref<HTMLButtonElement>;
typetype?: "button" | "submit" | "reset" | undefined;

ToggleOwnProps

Import from @askrjs/ui/toggle.

PropTypeDefaultDescription
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((e: PressEvent) => void) | undefined;
pressedpressed?: boolean | undefined;

ToggleProps

Import from @askrjs/ui/toggle.

PropTypeDefaultDescription
asChildasChild?: boolean | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((e: PressEvent) => void) | undefined;
pressedpressed?: boolean | undefined;
refref?: ((value: Element | null) => void) | { current: Element | null; } | ((value: HTMLButtonElement | null) => void) | { current: HTMLButtonElement | null; } | null | undefined;
typetype?: "button" | "submit" | "reset" | undefined;

ToggleGroupItemAsChildProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<Element>;
typetype?: undefined;
valuevalue: string;

ToggleGroupItemOwnProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
valuevalue: string;

ToggleGroupItemProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<HTMLButtonElement>;
typetype?: "button" | "submit" | "reset" | undefined;

ToggleGroupMultipleProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;Supports literal, nested, array-mapped, and `For`-rendered descendants.
defaultValuedefaultValue?: string[] | undefined;
disableddisabled?: boolean | undefined;
idid?: string | undefined;
looploop?: boolean | undefined;
onValueChangeonValueChange?: ((value: string[]) => void) | undefined;
orientationorientation?: ToggleGroupOrientation | undefined;
refref?: Ref<HTMLDivElement>;
typetype: "multiple";
valuevalue?: string[] | undefined;

ToggleGroupProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;Supports literal, nested, array-mapped, and `For`-rendered descendants.
defaultValuedefaultValue?: string | string[] | undefined;
disableddisabled?: boolean | undefined;
idid?: string | undefined;
looploop?: boolean | undefined;
onValueChangeonValueChange?: ((value: string[]) => void) | ((value: string) => void) | undefined;
orientationorientation?: ToggleGroupOrientation | undefined;
refref?: Ref<HTMLDivElement>;
typetype?: "single" | "multiple" | undefined;
valuevalue?: string | string[] | undefined;

ToggleGroupSingleProps

Import from @askrjs/ui/toggle-group.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;Supports literal, nested, array-mapped, and `For`-rendered descendants.
defaultValuedefaultValue?: string | undefined;
disableddisabled?: boolean | undefined;
idid?: string | undefined;
looploop?: boolean | undefined;
onValueChangeonValueChange?: ((value: string) => void) | undefined;
orientationorientation?: ToggleGroupOrientation | undefined;
refref?: Ref<HTMLDivElement>;
typetype?: "single" | undefined;
valuevalue?: string | undefined;

Purpose

This page covers two related but distinct primitives: `Toggle`, a single pressed/unpressed button (bold, italic, favorite-star patterns), and `ToggleGroup`, a set of toggle buttons where selecting one can affect the others — either a single-select group like alignment buttons or a multi-select group like text formatting. They share the same pressed-state model but solve different layout problems, which is why they're documented together rather than as a combined component.

Install and import

Both primitives ship from `@askrjs/ui` with their own subpaths — `@askrjs/ui/toggle` and `@askrjs/ui/toggle-group` — and matching `@askrjs/themes/toggle` and `@askrjs/themes/toggle-group` JS subpaths that re-export the same components; there's no separate `toggle.css` file, just `toggle-group.css` in the default theme covering both, loaded the same way as every other component's styling (via `@askrjs/themes/default`, not per-subpath). Import only the JS subpath you need; a page that just needs a single toggle button doesn't have to pull in the group's item-collection logic.

Live examples

The examples include a standalone `Toggle` for a single boolean action, a single-select `ToggleGroup` (`type="single"`) for mutually exclusive choices like text alignment, and a multi-select `ToggleGroup` (`type="multiple"`) for independent choices like bold/italic/underline stacked together.

Anatomy

`Toggle` is a standalone component with no required children structure beyond its own content. `ToggleGroup` is a root that wraps one or more `ToggleGroupItem` children, each identified by a required `value` string — the group reads its items' values to compute what's selected rather than you tracking indices.

State model

`Toggle`'s `pressed` prop is deliberately controlled-only — there's no `defaultPressed` or internal fallback, so you own the boolean and respond to `onPress`. `ToggleGroup` splits on its `type` discriminant: `type="single"` (the default) uses a string `value`/`defaultValue`/`onValueChange`, while `type="multiple"` switches all three to arrays of strings. Passing the wrong shape for the chosen `type` is a type error, not a runtime surprise.

Keyboard and accessibility

`Toggle` uses `role="button"` with `aria-pressed` and responds to both Enter and Space, and its types explicitly prohibit `onClick` in favor of `onPress` — the package's own docs note this is because `onClick` doesn't carry toggle or disabled semantics the way the pressable foundation's `onPress` does. `ToggleGroup` exposes a `loop` prop that controls whether arrow-key navigation wraps from the last item back to the first, and the group itself carries `role="group"` while each item keeps `role="button"` with `aria-pressed`.

Styling and tokens

Both components expose `data-state` (`on`/`off` for Toggle, similar semantics per item in a group) and `data-disabled` for CSS targeting, plus `data-orientation` on `ToggleGroup` so horizontal and vertical layouts can be styled independently. `@askrjs/themes` applies the shared button-like visual language here, so a themed `Toggle` looks consistent with `Button` at rest and simply adds a pressed treatment.

API

`Toggle` takes `pressed`, `onPress`, `disabled`, and the usual `asChild` escape hatch. `ToggleGroup` takes `type`, the value props matching that type, `orientation`, `loop`, and `disabled` (which cascades to disable the whole group); `ToggleGroupItem` takes its own `value`, optional `disabled`, and `asChild`, letting individual items opt out independently of the group-level flag.

Edge cases

Disabling `ToggleGroup` at the root disables every item regardless of each item's own `disabled` prop, but the reverse isn't true — you can disable individual items in an otherwise enabled group. Because `ToggleGroupItem`'s `value` is required, forgetting to set one is a compile-time error rather than something that silently breaks selection at runtime.

For a single always-visible on/off control tied to a setting rather than a pressed visual, Switch is usually the better fit. If your toggle group is really acting as a segmented tab selector, look at Tabs instead — the visual overlap is common but the semantics differ.