Skip to main content
Vuetify0 is now in alpha!
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).

Roving Focus Architecture

Use controls to zoom and pan. Click outside or press Escape to close.

Roving Focus Architecture

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

Color Grid

2D grid navigation with the columns option. Arrow keys navigate in two dimensions, Home/End are row-scoped, Ctrl+Home/End jump to absolute first/last.

FileRole
Grid.vueColor swatch grid with 2D keyboard navigation
grid.vueEntry point rendering a material color palette

API Reference

The following API details are for the useRovingFocus composable.

Functions

useRovingFocus

(items: () => RovingItem[], options?: RovingFocusOptions) => RovingFocusReturn

Options

target

MaybeRefOrGetter<HTMLElement | null | undefined>

orientation

"horizontal" | "vertical" | "both" | undefined

Arrow key mapping. Ignored when `columns` is set (grid uses all 4 arrows).

circular

boolean | undefined

columns

MaybeRefOrGetter<number> | undefined

Column count for grid navigation. When set, items are treated as a 2D grid in row-major order: Left/Right step ±1, Up/Down step ±columns, Home/End go to row start/end, Ctrl+Home/End go to first/last overall.

onFocus

((id: ID) => void) | undefined

Properties

focusedId

ShallowRef<ID | undefined>

Methods

isTabbable

(id: ID) => boolean

focus

(id: ID) => void

next

() => void

prev

() => void

first

() => void

last

() => void

onKeydown

(e: KeyboardEvent) => void
Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/