useOutsideClick
Detects clicks outside of a specified DOM element, useful for closing modals or dropdowns.
Code
1import { useEffect, RefObject } from "react"
2
3export function useOutsideClick<T extends HTMLElement = HTMLElement>(
4 ref: RefObject<T>,
5 handler: (event: MouseEvent | TouchEvent) => void,
6): void {
7 useEffect(() => {
8 const listener = (event: MouseEvent | TouchEvent) => {
9 if (!ref.current || ref.current.contains(event.target as Node)) {
10 return
11 }
12 handler(event)
13 }
14
15 document.addEventListener("mousedown", listener)
16 document.addEventListener("touchstart", listener)
17
18 return () => {
19 document.removeEventListener("mousedown", listener)
20 document.removeEventListener("touchstart", listener)
21 }
22 }, [ref, handler])
23}
Usage Example
import { useRef, useState } from "react"
import { useOutsideClick } from "@/hooks/use-outside-click"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
export function OutsideClickExample() {
const [isOpen, setIsOpen] = useState(false)
const menuRef = useRef(null)
useOutsideClick(menuRef, () => {
if (isOpen) setIsOpen(false)
})
return (
<div className="relative">
<Button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? "Close Menu" : "Open Menu"}
</Button>
{isOpen && (
<Card ref={menuRef} className="absolute mt-2 p-4 w-48 bg-npui-card border-npui-border shadow-lg z-10">
<CardContent className="p-0">
<ul className="space-y-2">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</CardContent>
</Card>
)}
<p className="mt-2 text-sm text-npui-muted-foreground">Click outside the menu to close it.</p>
</div>
)
}