Toggle Group

A set of two-state buttons that can be toggled on or off.

Examples

Basic

PropDefaultTypeDescription
items[]Array<T>The items to display in the toggle group.
defaultValue-AcceptableValue | Array<AcceptableValue>The default active value of the item(s).
disabledfalsebooleanWhen set to true, disables user interaction with the toggle group and all its items.
modelValue-AcceptableValue | Array<AcceptableValue>The controlled value of the active item(s), which can be bind using v-model.
name-stringThe name of the field. Submitted with its owning form as part of a name/value pair.
orientation-vertical | horizontalThe orientation of the component, which determines how focus moves.
required-booleanWhen true, indicates that the user must set the value before the owning form can be submitted.
rovingFocustruebooleanWhen false, navigating through the items using arrow keys will be disabled.
type-single | multipleDetermines whether a "single" or "multiple" items can be selected at a time.
Preview
Code

Orientation

PropDefaultTypeDescription
orientationhorizontalhorizontal, verticalSet the orientation of the toggle-group.
Preview
Code
Vertical
Horizontal (Default)

Type

PropDefaultTypeDescription
type-single, multipleSet the type of the toggle-group.
Preview
Code

Variant and Color

PropDefaultTypeDescription
toggle-onsoft-accent{variant}-{color}Change the color of the toggle when item is on.
toggle-offghost-gray{variant}-{color}Change the color of the toggle when item is off.
Preview
Code

Size

Adjust the toggle-group size without limits. Use breakpoints (e.g., sm:sm, xs:lg) for responsive sizes or states (e.g., hover:lg, focus:3xl) for state-based sizes.

PropDefaultTypeDescription
sizesmstringAdjusts the overall size of the toggle-group component.
_toggleGroupItem.sizesmstringCustomizes the size of each item within the toggle-group.
Preview
Code

Slots

NamePropsDescription
defaultmodelValueThe default slot, overrides everything.
#{{ item.slot }}item, valueThe item dynamic slot.
itemitem, activeThe item static slot.
Preview
Code

Custom Rendering

Use the default slot for full control over the toggle-group's structure. This allows you to compose the toggle-group using its individual sub-components (like NToggleGroup, NToggleGroupItem, listed in the Components section), similar to libraries like shadcn/ui.

Preview
Code

Presets

shortcuts/toggle-group.ts
type ToggleGroupPrefix = 'toggle-group'

export const staticToggleGroup: Record<`${ToggleGroupPrefix}-${string}` | ToggleGroupPrefix, string> = {
  // configurations
  'toggle-group': 'flex gap-1 flex-wrap w-fit rounded-md',
  'toggle-group-horizontal': 'flex-row items-center',
  'toggle-group-vertical': 'flex-col items-start',

  // components
  'toggle-group-item': '',
}

export const dynamicToggleGroup: [RegExp, (params: RegExpExecArray) => string][] = []

export const toggleGroup = [
  staticToggleGroup,
  ...dynamicToggleGroup,
]

Props

types/toggle-group.ts
import type { ToggleGroupItemProps, ToggleGroupRootProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'
import type { NToggleProps } from './toggle'

interface BaseExtensions {
  /** CSS class for the component */
  class?: HTMLAttributes['class']
  /** Size of the component */
  size?: HTMLAttributes['class']
}

export interface NToggleGroupProps<T> extends ToggleGroupRootProps, Pick<NToggleProps, 'toggleOff' | 'toggleOn'>, BaseExtensions {
  /**
   * The array of items that is passed to the toggle group.
   *
   * @default []
   */
  items?: T

  /** Props for the toggle group item */
  _toggleGroupItem?: Partial<NToggleGroupItemProps>

  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/toggle-group.ts
   */
  una?: NToggleGroupUnaProps
}
export interface NToggleGroupItemProps extends ToggleGroupItemProps, Omit<NToggleProps, 'una'>, Pick<BaseExtensions, 'class'> {
  /** Slot of the toggle group item */
  slot?: string
  /** Additional properties for the una component */
  una?: Pick<NToggleGroupUnaProps, 'toggleGroupItem'> & NButtonProps['una']
}

interface NToggleGroupUnaProps {
  /** CSS class for the navigation menu */
  toggleGroup?: HTMLAttributes['class']
  /** CSS class for the navigation menu content */
  toggleGroupItem?: HTMLAttributes['class']
}

Components

ToggleGroup.vue
ToggleGroupItem.vue
<script lang="ts">
import type { ToggleGroupRootEmits } from 'reka-ui'
import type { NToggleGroupItemProps, NToggleGroupProps } from '../../types'
</script>

<script setup lang="ts" generic="T extends NToggleGroupItemProps, U extends Array<T>">
import { reactiveOmit } from '@vueuse/core'
import { ToggleGroupRoot, useForwardPropsEmits } from 'reka-ui'
import { cn } from '../../utils'
import ToggleGroupItem from './ToggleGroupItem.vue'

const props = defineProps<NToggleGroupProps<U>>()
const emits = defineEmits<ToggleGroupRootEmits>()

const rootProps = reactiveOmit(props, [
  'class',
  'size',
  'toggleOff',
  'toggleOn',
  'una',
])

const forwarded = useForwardPropsEmits(rootProps, emits)
</script>

<template>
  <ToggleGroupRoot
    v-slot="slotProps"
    v-bind="forwarded"
    data-slot="toggle-group"
    :class="cn(
      'toggle-group',
      props.orientation === 'vertical' ? 'toggle-group-vertical' : 'toggle-group-horizontal',
      props.class,
      props.una?.toggleGroup,
    )"
  >
    <slot v-bind="slotProps">
      <template v-if="items && items.length">
        <template v-for="item in items" :key="item.value">
          <slot :name="item.slot ?? 'item'" v-bind="{ item, value: item.value }">
            <ToggleGroupItem
              :una
              :size
              :toggle-on
              :toggle-off
              v-bind="{ ...(item as NToggleGroupItemProps), ..._toggleGroupItem }"
            />
          </slot>
        </template>
      </template>
    </slot>
  </ToggleGroupRoot>
</template>