Appearance
Vue Autofocus Directive
A custom Vue directive that automatically focuses the first focusable element and selects its text content on mount. When applied to a wrapper element (like a form or div), it finds the first focusable descendant rather than requiring direct application to the input.
When to use
Use this directive on any element that should receive focus when it mounts — dialogs, form fields, or wrapper elements. It finds the first focusable descendant, focuses it, and selects its text so the user can immediately start typing a replacement.
The pattern
Apply v-autofocus to any element. The directive finds the first focusable descendant, focuses it, and selects its text content.
On a form
The first <input> inside the form receives focus automatically.
vue
<template>
<form v-autofocus @submit.prevent="form.submit()">
<Input label="Name" v-model="form.data.name" />
<Input label="Email" v-model="form.data.email" />
<button type="submit">Save</button>
</form>
</template>On an input directly
When applied directly to an input, querySelector finds the element itself (it matches input), focuses it, and selects any existing text.
vue
<template>
<input v-autofocus v-model="search" />
</template>In a dialog
The directive works on any container, including <dialog> elements that render conditionally.
vue
<template>
<dialog v-if="open" v-autofocus>
<Input
label="Account Name"
v-model="form.data.name"
/>
<button @click="close">Cancel</button>
</dialog>
</template>Implementing the autofocus directive
Use a mounted directive hook that queries for focusable elements, focuses the first match, and optionally selects its text.
javascript
const AutofocusDirective = {
mounted(element) {
const focusableSelector = `
button, [href], input, select, textarea,
[tabindex]:not([tabindex="-1"])
`;
const focusable = element.querySelector(focusableSelector);
focusable?.focus?.();
focusable?.select?.();
},
};Register the directive globally on the app instance for use in any template, or locally in <script setup> by assigning it to a v-prefixed variable — Vue resolves vAutofocus as the v-autofocus directive automatically.
javascript
// Global registration
app.directive("autofocus", AutofocusDirective);
// Local registration in <script setup>
const vAutofocus = AutofocusDirective;Descendant search
The directive doesn't assume element is focusable. It queries for the first focusable descendant using a standard selector that covers buttons, links, inputs, selects, textareas, and elements with explicit tabindex. Elements with tabindex="-1" are programmatically focusable but not in the tab order, so they're excluded from the auto-focus search.
Optional chaining guards
focusable?.focus?.() and focusable?.select?.() guard against null matches and elements that don't support select() (like buttons). No error is thrown if there's nothing to focus.
Mounted-only behavior
The directive runs once when the element enters the DOM. It doesn't re-focus on updates. For re-focusing on visibility changes, use a watcher instead.
Trade-offs
- First focusable only — Always focuses the first match. If the desired target isn't the first focusable element, reorder the DOM or apply the directive directly to the target element.
- No delay option — Focus happens synchronously in
mounted. If the element is inside a transition or animation, it may receive focus before it's visually ready. UsenextTickorsetTimeoutin a custom variant if timing is an issue. select()on all inputs — Callsselect()on the focused element, which selects all text in inputs. This is helpful for edit forms but unexpected for empty inputs (where it's a no-op anyway).