useClickOutside Hook
April 26, 2025
Detecting clicks outside an element is a common pattern for closing modals, dropdowns, or tooltips. This hook makes it easy to handle this interaction.
import { useEffect, RefObject } from 'react';
export function useClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
handler: (event: MouseEvent | TouchEvent) => void
): void {
useEffect(() => {
const listener = (event: MouseEvent | TouchEvent) => {
const el = ref?.current;
if (!el || el.contains(event.target as Node)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}
How It Works
- Listens for mousedown and touchstart events on the document.
- Checks if the click target is outside the referenced element.
- Calls the handler if the click is outside.
- Cleans up event listeners on unmount.
Example Usage
function Dropdown() {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useClickOutside(dropdownRef, () => setIsOpen(false));
return (
<div ref={dropdownRef}>
<button onClick={() => setIsOpen(!isOpen)}>
Toggle Menu
</button>
{isOpen && (
<div className="dropdown-menu">
<a href="/">Home</a>
<a href="/about">About</a>
</div>
)}
</div>
);
}
Modal Example
function Modal({ onClose, children }) {
const modalRef = useRef<HTMLDivElement>(null);
useClickOutside(modalRef, onClose);
return (
<div className="modal-overlay">
<div ref={modalRef} className="modal-content">
{children}
</div>
</div>
);
}
Use Cases
- Closing modals when clicking outside
- Collapsing dropdown menus
- Dismissing tooltips
- Closing popover menus
- Hiding context menus
Notes
- Works with both mouse and touch events.
- The handler should be memoized with
useCallbackto avoid unnecessary re-renders. - Make sure the ref is attached to the element you want to detect clicks outside of.
A clean way to handle the "click outside" pattern that's essential for modern UI components.