Skip to main content
Vuetify0 is now in beta!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

useRovingFocus

Keyboard navigation for composite widgets where arrow keys move focus between items, skipping disabled ones.

Usage

useRovingFocus manages focus across a group of items — only the active item has tabindex="0", all others have tabindex="-1". Arrow keys move focus between items, automatically skipping disabled ones. Supports linear (horizontal/vertical) and grid (2D) navigation modes.

vue
<script setup lang="ts">
  import { useRovingFocus } from '@vuetify/v0'
  import { useTemplateRef } from 'vue'

  const toolbar = useTemplateRef('toolbar')

  const items = [
    { id: 'bold', label: 'Bold' },
    { id: 'italic', label: 'Italic' },
    { id: 'underline', label: 'Underline', disabled: true },
    { id: 'strike', label: 'Strikethrough' },
  ]

  const { focusedId, isTabbable } = useRovingFocus(
    () => items.map(item => ({
      id: item.id,
      el: () => toolbar.value?.querySelector(`[data-id="${item.id}"]`),
      disabled: item.disabled,
    })),
    { target: toolbar, orientation: 'horizontal' },
  )
</script>

<template>
  <div ref="toolbar" role="toolbar" aria-label="Formatting">
    <button
      v-for="item in items"
      :key="item.id"
      :data-id="item.id"
      :tabindex="isTabbable(item.id) ? 0 : -1"
      :disabled="item.disabled"
    >
      {{ item.label }}
    </button>
  </div>
</template>

useRovingFocus vs useVirtualFocus

Both manage keyboard navigation, but they use different focus strategies:

useRovingFocususeVirtualFocus
DOM focusMoves to each itemStays on the control (e.g., <input>)
tabindexManaged per itemNot used
ARIAItems receive focus directlyaria-activedescendant on control
Use forToolbars, menus, grids, tabsComboboxes, autocompletes, searchable selects
Keyboard patternItems are real focusable elementsItems are virtual — only one DOM node has focus

Choose useRovingFocus when items are real interactive elements (buttons, links). Choose useVirtualFocus when a single input drives a list of options that aren’t individually focusable.

Architecture

useRovingFocus builds on useEventListener for keydown handling. It is a standalone composable — not part of the registry/selection hierarchy — making it composable alongside createSingle or createSelection for widgets that separate focus from selection (e.g., listboxes, selects).

Reactivity

Property/MethodReactiveNotes
focusedIdShallowRef, tracks currently focused item
isTabbable(id)-Returns true for the one item that should have tabindex="0"
focus(id)-Programmatically focus an item by ID
next()-Move focus to next enabled item
prev()-Move focus to previous enabled item
first()-Move focus to first enabled item
last()-Move focus to last enabled item
onKeydown-Keydown handler — auto-bound when target is provided

Examples

API Reference

The following API details are for the useRovingFocus composable.
Was this page helpful?

Ctrl+/