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.
<script setup lang="ts">
import { useClickOutside } from '@vuetify/v0'
import { useTemplateRef } from 'vue'
const menu = useTemplateRef('menu')
useClickOutside(menu, () => {
console.log('Clicked outside the menu')
})
</script>
<template>
<div ref="menu">
Menu content
</div>
</template>Architecture
useClickOutside builds on useEventListener for pointer and focus event detection:
Options
| Option | Type | Default | Description |
|---|---|---|---|
bounds | boolean | false | Use bounding-rect detection instead of DOM containment. Required for native <dialog> elements — backdrop clicks have the <dialog> as the event target, so containment checks always pass |
import { useClickOutside } from '@vuetify/v0'
const dialog = useTemplateRef('dialog')
// For native <dialog> — backdrop clicks are detected via coordinates
useClickOutside(dialog, () => dialog.value?.close(), { bounds: true })Reactivity
| Property/Method | Reactive | Notes |
|---|---|---|
isActive | Computed from !isPaused | |
isPaused | ShallowRef, readonly | |
pause() | - | Stop detection, preserve state |
resume() | - | Resume detection |
stop() | - | Stop and clean up listeners |
Examples
Recipes
Multiple Targets
Pass an array of refs to ignore clicks inside any of them:
import { useClickOutside } from '@vuetify/v0'
import { useTemplateRef } from 'vue'
const trigger = useTemplateRef('trigger')
const panel = useTemplateRef('panel')
// Clicks inside EITHER trigger or panel are ignored
useClickOutside([trigger, panel], () => {
console.log('Clicked outside both elements')
})The target parameter accepts MaybeArray<ClickOutsideTarget> — a single ref/getter or an array of refs/getters.
FAQ
Backdrop clicks report the <dialog> itself as the event target, so DOM-containment checks always pass. Pass { bounds: true } to switch to bounding-rect detection, which tests the click coordinates against the element’s rectangle instead.
Pass an array of targets — useClickOutside([trigger, panel], cb) — and clicks inside either are treated as inside. Alternatively wrap the trigger and panel in one element and pass that single ref.
Detection is two-phase (pointerdown → pointerup) and both must land outside. A drag that starts inside and releases outside is ignored, which keeps text selection and slider drags from triggering a false dismiss.
Call pause() to stop detection while preserving state, then resume() to re-enable it; stop() removes the listeners for good. isActive reflects whether detection is currently running.