Detect clicks outside a specific element, useful for closing modals and dropdowns.
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]);
}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]);
}
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>
);
}
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>
);
}
useCallback to avoid unnecessary re-renders.A clean way to handle the "click outside" pattern that's essential for modern UI components.