useClickOutside
A composable for detecting clicks outside of specified element(s) with automatic cleanup.
Usage
The useClickOutside composable detects when users click outside target elements. It uses two-phase detection (pointerdown → pointerup) to prevent false positives when dragging, and includes touch scroll handling for mobile.
Examples
With Component Refs
When using component refs (like Atom), pass a getter that returns the exposed element:
import { Atom, useClickOutside } from '@vuetify/v0'
import { ref, useTemplateRef } from 'vue'
const isOpen = ref(true)
const popoverRef = useTemplateRef<AtomExpose>('popover')
useClickOutside(
() => popoverRef.value?.element,
() => { isOpen.value = false }
)Multiple Targets
Detect clicks outside multiple elements (e.g., anchor and popover):
import { useClickOutside } from '@vuetify/v0'
import { ref, useTemplateRef } from 'vue'
const isOpen = ref(false)
const anchorRef = useTemplateRef<HTMLElement>('anchor')
const popoverRef = useTemplateRef<AtomExpose>('popover')
useClickOutside(
[anchorRef, () => popoverRef.value?.element],
() => { isOpen.value = false }
)Ignoring Elements
Ignore specific elements via CSS selectors or refs:
import { useClickOutside } from '@vuetify/v0'
import { useTemplateRef } from 'vue'
const menuRef = useTemplateRef<HTMLElement>('menu')
useClickOutside(menuRef, close, {
ignore: ['[data-app-bar]', '.toast-container']
})Pause and Resume
Control detection programmatically:
const { pause, resume, stop, isPaused } = useClickOutside(menuRef, close)
// Temporarily disable during animations
pause()
// Re-enable after animation
resume()
// Permanently stop and cleanup
stop()Accessibility
This composable handles pointer interactions only. For accessible overlays, you must implement additional patterns.
Keyboard Dismissal (Required)
WCAG 2.1.2 Level A: Without keyboard dismissal, click-outside creates a keyboard trap. This is a compliance failure.
import { useClickOutside, useKeydown } from '@vuetify/v0'
// Pointer dismissal
useClickOutside(menuRef, close)
// Keyboard dismissal - REQUIRED for WCAG 2.1.2
useKeydown({ key: 'Escape', handler: close })Focus Restoration
Return focus to the trigger element after dismissal per APG dialog patterns:
import { nextTick } from 'vue'
function close() {
isOpen.value = false
// Return focus to trigger for screen reader context
nextTick(() => buttonRef.value?.focus())
}
useClickOutside(menuRef, close)Mobile Screen Reader Support
Mobile screen reader users (VoiceOver, TalkBack) cannot press Escape. Include a focusable close button within overlays:
<div ref="menu" role="menu">
<!-- Visually hidden close for screen readers -->
<button class="sr-only" @click="close" aria-label="Close menu">
Close
</button>
<!-- menu items -->
</div>The sr-only class hides content visually while keeping it accessible:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}API
useClickOutside
Type
tsinterface UseClickOutsideOptions { capture?: boolean // Use capture phase (default: true) touchScrollThreshold?: number // Touch scroll threshold in px (default: 30) detectIframe?: boolean // Detect iframe focus (default: false) ignore?: MaybeRefOrGetter<ClickOutsideIgnoreTarget[]> } interface UseClickOutsideReturn { readonly isActive: Readonly<Ref<boolean>> readonly isPaused: Readonly<Ref<boolean>> pause: () => void resume: () => void stop: () => void } function useClickOutside( target: ClickOutsideTarget | readonly ClickOutsideTarget[], handler: (event: PointerEvent | FocusEvent) => void, options?: UseClickOutsideOptions ): UseClickOutsideReturnDetails
Uses two-phase detection (pointerdown → pointerup) to prevent false positives when users drag from inside to outside an element. Touch interactions that move more than the threshold are treated as scrolls, not clicks.
Parameters
target: Element ref(s) to detect clicks outside ofhandler: Callback invoked when a click outside is detectedoptions:capture: Use capture phase for event listeners (default: true)touchScrollThreshold: Pixel threshold for touch scroll detection (default: 30)detectIframe: Detect focus moving to iframes outside target (default: false)ignore: Elements or CSS selectors to ignore
Returns
isActive: Whether detection is currently activeisPaused: Whether detection is pausedpause(): Pause detection (clears pending state)resume(): Resume detectionstop(): Stop detection and clean up
