ToggleButtonGroup

Groups multiple ToggleButtons into a unified control, allowing users to select one or multiple options.

Usage

import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <ToggleButtonGroup selection-mode="multiple" separator>    <ToggleButton icon-only aria-label="Bold" value="bold">      <Icon icon="gravity-ui:bold" />    </ToggleButton>    <ToggleButton icon-only aria-label="Italic" value="italic">      <Icon icon="gravity-ui:italic" />    </ToggleButton>    <ToggleButton icon-only aria-label="Underline" value="underline">      <Icon icon="gravity-ui:underline" />    </ToggleButton>    <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">      <Icon icon="gravity-ui:strikethrough" />    </ToggleButton>  </ToggleButtonGroup></template>

Anatomy

<template>
  <ToggleButtonGroup selection-mode="multiple" separator>
    <ToggleButton value="first">First</ToggleButton>
    <ToggleButton value="second">Second</ToggleButton>
    <ToggleButton value="third">Third</ToggleButton>
  </ToggleButtonGroup>
</template>

HeroUI's ToggleButtonGroup.Separator, placed inside each ToggleButton except the first, becomes the separator prop: every ToggleButton renders the divider and the first one hides it. Each ToggleButton's value (HeroUI's id) is what the group's v-model holds.

ui keys: base, separator, plus button, which is added to each ToggleButton in the group.

Examples

Sizes

Small
Medium (default)
Large
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="flex flex-col gap-6">    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Small</span>      <ToggleButtonGroup selection-mode="multiple" separator size="sm">        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Medium (default)</span>      <ToggleButtonGroup selection-mode="multiple" separator size="md">        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Large</span>      <ToggleButtonGroup selection-mode="multiple" separator size="lg">        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>  </div></template>

Orientation

Horizontal
Vertical
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="flex items-start gap-8">    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Horizontal</span>      <ToggleButtonGroup orientation="horizontal" selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Vertical</span>      <ToggleButtonGroup orientation="vertical" selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>      </ToggleButtonGroup>    </div>  </div></template>

Full Width

<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="w-full max-w-md space-y-3">    <ToggleButtonGroup full-width selection-mode="multiple" separator>      <ToggleButton icon-only aria-label="Bold" value="bold">        <Icon icon="gravity-ui:bold" />      </ToggleButton>      <ToggleButton icon-only aria-label="Italic" value="italic">        <Icon icon="gravity-ui:italic" />      </ToggleButton>      <ToggleButton icon-only aria-label="Underline" value="underline">        <Icon icon="gravity-ui:underline" />      </ToggleButton>      <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">        <Icon icon="gravity-ui:strikethrough" />      </ToggleButton>    </ToggleButtonGroup>    <ToggleButtonGroup full-width selection-mode="single" separator>      <ToggleButton value="left">        <Icon icon="gravity-ui:text-align-left" />        Left      </ToggleButton>      <ToggleButton value="center">        <Icon icon="gravity-ui:text-align-center" />        Center      </ToggleButton>      <ToggleButton value="right">        <Icon icon="gravity-ui:text-align-right" />        Right      </ToggleButton>    </ToggleButtonGroup>  </div></template>

Disabled

All buttons disabled
Individual button disabled
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="flex flex-col gap-6">    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">All buttons disabled</span>      <ToggleButtonGroup disabled selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Individual button disabled</span>      <ToggleButtonGroup selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton disabled icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>      </ToggleButtonGroup>    </div>  </div></template>

Without Separator

Omit the separator prop to render buttons without dividers.

<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <ToggleButtonGroup selection-mode="multiple">    <ToggleButton icon-only aria-label="Bold" value="bold">      <Icon icon="gravity-ui:bold" />    </ToggleButton>    <ToggleButton icon-only aria-label="Italic" value="italic">      <Icon icon="gravity-ui:italic" />    </ToggleButton>    <ToggleButton icon-only aria-label="Underline" value="underline">      <Icon icon="gravity-ui:underline" />    </ToggleButton>    <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">      <Icon icon="gravity-ui:strikethrough" />    </ToggleButton>  </ToggleButtonGroup></template>

Detached

Use detached to separate buttons with gaps instead of connecting them.

Attached (default)
Detached
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="flex flex-col gap-6">    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Attached (default)</span>      <ToggleButtonGroup selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Detached</span>      <ToggleButtonGroup detached selection-mode="multiple">        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>  </div></template>

Selection Mode

Use selection-mode="single" for mutually exclusive choices or selection-mode="multiple" for independent toggles.

Single selection
Multiple selection
<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <div class="flex flex-col gap-6">    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Single selection</span>      <ToggleButtonGroup :default-value="['center']" selection-mode="single" separator>        <ToggleButton value="left">          <Icon icon="gravity-ui:text-align-left" />          Left        </ToggleButton>        <ToggleButton value="center">          <Icon icon="gravity-ui:text-align-center" />          Center        </ToggleButton>        <ToggleButton value="right">          <Icon icon="gravity-ui:text-align-right" />          Right        </ToggleButton>      </ToggleButtonGroup>    </div>    <div class="flex flex-col gap-2">      <span class="text-sm text-muted">Multiple selection</span>      <ToggleButtonGroup :default-value="['bold', 'underline']" selection-mode="multiple" separator>        <ToggleButton icon-only aria-label="Bold" value="bold">          <Icon icon="gravity-ui:bold" />        </ToggleButton>        <ToggleButton icon-only aria-label="Italic" value="italic">          <Icon icon="gravity-ui:italic" />        </ToggleButton>        <ToggleButton icon-only aria-label="Underline" value="underline">          <Icon icon="gravity-ui:underline" />        </ToggleButton>        <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">          <Icon icon="gravity-ui:strikethrough" />        </ToggleButton>      </ToggleButtonGroup>    </div>  </div></template>

Controlled

Selected: bold

<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'import { ref } from 'vue'const selected = ref(['bold'])</script><template>  <div class="flex flex-col gap-4">    <ToggleButtonGroup v-model="selected" selection-mode="multiple" separator>      <ToggleButton icon-only aria-label="Bold" value="bold">        <Icon icon="gravity-ui:bold" />      </ToggleButton>      <ToggleButton icon-only aria-label="Italic" value="italic">        <Icon icon="gravity-ui:italic" />      </ToggleButton>      <ToggleButton icon-only aria-label="Underline" value="underline">        <Icon icon="gravity-ui:underline" />      </ToggleButton>      <ToggleButton icon-only aria-label="Strikethrough" value="strikethrough">        <Icon icon="gravity-ui:strikethrough" />      </ToggleButton>    </ToggleButtonGroup>    <p class="text-sm text-muted">      Selected:      <span class="font-medium">{{ selected.length > 0 ? selected.join(', ') : 'None' }}</span>    </p>  </div></template>

Customization

Tailwind CSS

<script setup lang="ts">import { Icon } from '@iconify/vue'import { ToggleButton, ToggleButtonGroup } from '@hareui/vue'</script><template>  <ToggleButtonGroup    aria-label="Text formatting"    class="gap-1 rounded-xl border border-border/80 bg-surface p-1 shadow-sm"    selection-mode="multiple"  >    <ToggleButton icon-only aria-label="Bold" class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground" value="bold">      <Icon icon="gravity-ui:bold" />    </ToggleButton>    <ToggleButton icon-only aria-label="Italic" class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground" value="italic">      <Icon icon="gravity-ui:italic" />    </ToggleButton>    <ToggleButton icon-only aria-label="Underline" class="rounded-lg text-muted data-[selected=true]:bg-accent-soft data-[selected=true]:text-accent-soft-foreground" value="underline">      <Icon icon="gravity-ui:underline" />    </ToggleButton>  </ToggleButtonGroup></template>

Global theme override

Override the toggleButtonGroup theme app-wide with createHareUI:

app.use(createHareUI({
  ui: {
    toggleButtonGroup: {
      slots: { base: 'rounded-lg', separator: 'opacity-25' },
      variants: { fullWidth: { true: { base: 'w-full' } } },
    },
  },
}))

Styling Reference

Each rendered element carries the same data-slot string as HeroUI, so selectors translate 1:1.

ui keydata-slotDescription
basetoggle-button-groupGroup container
separatortoggle-button-group-separatorDivider inside each ToggleButton
buttontoggle-buttonClasses added to each grouped ToggleButton

API Reference

Props

PropTypeDefaultDescription
asAsTag | Component"div"The element or component to render as.
modelValuestring[]-The values of the selected ToggleButtons (controlled, v-model).
defaultValuestring[][]The initially selected values when uncontrolled.
selectionMode"multiple" | "single""single"Whether one or several buttons can be selected. Single selection makes the buttons radios.
disallowEmptySelectionbooleanfalseWhether the last selected button can't be deselected.
orientation"horizontal" | "vertical"'horizontal'Layout direction, also the arrow keys that move focus. Inside a Toolbar it defaults to the toolbar's orientation.
size"md" | "sm" | "lg"-Size applied to every ToggleButton that doesn't set its own.
disabledbooleanfalseDisables every ToggleButton in the group; a ToggleButton's own :disabled="false" re-enables it.
fullWidthbooleanfalseWhether the group stretches to its container and the buttons share the width.
detachedbooleanfalseSeparates the buttons with a gap and fully rounds each one instead of joining them.
separatorbooleanfalseDraws a divider at the start of every button except the first (HeroUI's ToggleButtonGroup.Separator). Hidden when detached.
uiComponentSlots<{ slots: { base: string; button: string; separator: string; }; variants: { orientation: { horizontal: { base: string; button: string; separator: string; }; vertical: { base: string; button: string; separator: string; }; }; fullWidth: { true: { base: string; button: string; }; false: { base: string; }; }; detached: { true: { base: string; button: string; separator: string; }; false: { base: string; }; }; }; defaultVariants: { orientation: string; fullWidth: boolean; detached: boolean; }; }>-Per-slot class overrides. button is added to each ToggleButton, separator to each divider.

Slots

SlotPropsDescription
default{ selected: string[]; }The ToggleButtons, each with a value.

Emits

EventPayloadDescription
update:modelValue[value: string[]]-

Note

  • ToggleButtonGroup passes size to all child ToggleButton components
  • Each ToggleButton needs a unique value that corresponds to the values in v-model / default-value
  • The disabled prop disables all child ToggleButtons. A button can't opt back in with :disabled="false": as in React Aria, the group's disabled wins
  • The component automatically handles border radius between buttons
  • Use disallow-empty-selection with selection-mode="single" to ensure one option is always selected

On this page

No Headings