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

useDelay

Schedule open and close transitions with start, stop, pause, and resume controls and a reactive view of the pending delay.

Usage

The useDelay composable is the scheduling primitive behind hover-driven UI like tooltips, popovers, and menus. It mirrors the useTimer lifecycle — start, stop, pause, resume, plus reactive isActive, isPaused, remaining — and adds direction tracking via isOpening and a promise that resolves once the delay elapses.

ts
import { useDelay } from '@vuetify/v0'

const delay = useDelay(isOpening => {
  isVisible.value = isOpening
}, {
  openDelay: 300,
  closeDelay: 200,
})

// Schedule an open
await delay.start(true)

// Schedule a close, with a minimum delay of 500ms
await delay.start(false, { minDelay: 500 })

// Pause and resume the in-flight delay
delay.pause()
delay.resume()

// Cancel any pending transition
delay.stop()
Tip

Reactive delays Both openDelay and closeDelay accept refs, getters, or plain values. Pass a getter to vary the delay based on input mode — for example, 0 for keyboard focus and 300 for hover.

Architecture

useDelay Lifecycle

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

useDelay Lifecycle

start() cancels any in-flight delay before scheduling the next one — and the previous promise resolves with the new direction so awaiting code observes the latest intent. stop() cancels without scheduling a replacement; the pending promise resolves with the current isOpening direction. The pending timer is cleared on scope disposal.

Reactivity

PropertyTypeDescription
start(isOpening: boolean, options?: UseDelayStartOptions) => Promise<boolean>Start a delay; resolves with isOpening
stop() => voidCancel any pending delay and reset state
pause() => voidPause the in-flight delay, preserving remaining time
resume() => voidResume from where pause left off
isActiveReadonly<Ref<boolean>>true while a delay is pending
isPausedReadonly<Ref<boolean>>true after pause(), false after resume() or stop()
remainingReadonly<Ref<number>>Milliseconds left until the pending transition fires
isOpeningReadonly<Ref<boolean>>Direction of the pending transition (true = opening)

Examples

Hover with Pause/Resume

Hover the target to schedule a 2000 ms open; leave it to schedule a 1500 ms close. The progress bar reflects remaining against the active direction, the badges surface every reactive flag, and the controls demonstrate pause, resume, and stop against the in-flight delay.

Reach for this pattern when you want a tooltip or popover that respects hover intent without flickering. Pause/Resume is the differentiator — without it, briefly leaving the target to interact with adjacent UI would restart the close countdown. The promise returned by start() lets you sequence side effects after the delay elapses without a second watch.

FileRole
basic.vueDemonstrates hover-driven open/close with pause/resume
Hover to open · leave to close
isActive: false
isPaused: false
isOpening: false
1500ms

Key Features

Lifecycle Vocabulary

useDelay exposes the same lifecycle surface as useTimer (start, stop, pause, resume, isActive, isPaused, remaining) and adds an isOpening direction flag. Consumers that already know useTimer get the same shape with one extra dimension — direction.

Reactive Delays

openDelay and closeDelay accept any MaybeRefOrGetter<number>. Resolution happens when start() is called, so updates after start do not affect the in-flight delay.

ts
import { shallowRef } from 'vue'

const fast = shallowRef(false)

const delay = useDelay({
  openDelay: () => fast.value ? 0 : 500,
  closeDelay: 200,
})

Minimum Delay Floor

Pass minDelay to start() to enforce a floor on the resolved delay. Useful for transient feedback like toasts where the close delay must not be shorter than the time the content has been visible.

ts
const delay = useDelay({ closeDelay: 200 })

await delay.start(true)
// User dismisses immediately — still hold for 500ms total
await delay.start(false, { minDelay: 500 })

Re-Start Behavior

Calling start() while another delay is pending cancels the previous timer and resolves the previous promise with the new direction. Awaiting code sees a single source of truth — the latest intent — instead of stale resolutions.

ts
delay.start(true)
// User leaves before openDelay elapses
delay.start(false) // previous promise resolves false, new close delay begins

Pause and Resume

Pause preserves the remaining delay; resume continues from where pause left off. Useful for hover-driven UI where the user briefly interacts with the panel itself and you don’t want the auto-close timer to keep counting.

ts
const delay = useDelay({ closeDelay: 1000 })

delay.start(false)

// 300ms later — user moves into the panel
delay.pause() // remaining ≈ 700ms

// User moves out
delay.resume() // fires after ~700 more ms

Automatic Cleanup

The pending timer clears on scope disposal — no manual cleanup needed.

ts
// Timer automatically clears when component unmounts
const delay = useDelay({ openDelay: 300 })

FAQ

API Reference

The following API details are for the useDelay composable.

Functions

useDelay

(callback?: ((isOpening: boolean) => void) | undefined, options?: UseDelayOptions) => UseDelayReturn

Schedule open and close transitions with configurable delays. Composes `useTimer` for the lifecycle (`start` / `stop` / `pause` / `resume` plus reactive `isActive`, `isPaused`, `remaining`) and adds direction tracking (`isOpening`) and promise-based resolution.

Options

openDelay

MaybeRefOrGetter<number> | undefined

Delay in milliseconds before the opening transition fires.

closeDelay

MaybeRefOrGetter<number> | undefined

Delay in milliseconds before the closing transition fires.

Properties

isActive

Readonly<Ref<boolean, boolean>>

Whether a delay is currently pending (started, not stopped).

isPaused

Readonly<Ref<boolean, boolean>>

Whether the pending delay is currently paused.

remaining

Readonly<Ref<number, number>>

Milliseconds remaining until the pending transition fires.

isOpening

Readonly<Ref<boolean, boolean>>

Direction of the pending transition (`true` = opening, `false` = closing).

Methods

start

(isOpening: boolean, options?: UseDelayStartOptions) => Promise<boolean>

Start a delay in the given direction. Restarts if already running. Resolves with `isOpening` once the delay elapses. If `start()` is called again before the previous resolves, the previous promise resolves with the new direction.

stop

() => void

Cancel any pending delay and reset state. Resolves the pending promise with the current direction.

pause

() => void

Pause the in-flight delay, preserving remaining time. No-op if not running.

resume

() => void

Resume from where pause left off. No-op if not paused.

Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/