Toggle group

Selects one visible option. Default joins toggles with an ink active state; subtle uses a gray rail, 3px gaps, and a soft active fill.

Set

Default

Choose the alignment for the heading.

Subtle

The caption uses a quieter control with the same selection behavior.

Use it for

  • One of two to five options that should all stay visible: alignment, view mode, period.
  • Default for joined controls with hairline seams. Subtle uses a soft rail, 3px inset and gaps, 4px corners, and a soft active fill without internal strokes.
  • Short labels of equal weight.

Not for

  • Many or long options; use Select.
  • Multiple selection; use several Toggle buttons.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { ToggleGroup } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/toggle-group.

2. Vendor the source

npx @noorddev/vlak-cli add toggle-group

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/toggle-group.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { useState } from "react";
import { ToggleGroup } from "@noorddev/vlak-react";

const [align, setAlign] = useState("left");

<ToggleGroup
  variant="subtle"
  aria-label="Alignment"
  options={[
    { value: "left", label: "Left" },
    { value: "center", label: "Center" },
    { value: "right", label: "Right" },
  ]}
  value={align}
  onValueChange={setAlign}
/>

ToggleGroup

One pressed at a time.

PropTypeDefaultDescription
optionsrequired{ value: string; label: ReactNode; }[]
valuestring
defaultValuestring
onValueChange(value: string) => void
variant"subtle" | "default""default"Subtle groups use a soft rail and individually rounded selected items.

Also accepts Omit<HTMLAttributes<HTMLDivElement>, "onChange">.

KeysDoes
TabMoves between the options
Enter, SpaceSelects the focused option
<div class="rs-toggle-group" role="group" aria-label="Default alignment"><button class="rs-toggle rs-toggle-grouped rs-toggle-pressed rs-toggle-grouped-on" aria-pressed="true">Left</button><button class="rs-toggle rs-toggle-grouped" aria-pressed="false">Center</button><button class="rs-toggle rs-toggle-grouped" aria-pressed="false">Right</button></div>
<div class="rs-toggle-group rs-toggle-group-subtle" role="group" aria-label="Subtle alignment"><button class="rs-toggle rs-toggle-grouped rs-toggle-pressed rs-toggle-grouped-on rs-toggle-subtle rs-toggle-subtle-pressed rs-toggle-grouped-subtle" aria-pressed="true">Left</button><button class="rs-toggle rs-toggle-grouped rs-toggle-subtle rs-toggle-grouped-subtle" aria-pressed="false">Center</button><button class="rs-toggle rs-toggle-grouped rs-toggle-subtle rs-toggle-grouped-subtle" aria-pressed="false">Right</button></div>
.rs-toggle-group.rs-toggle-group-subtle