useOnClickOutside
A hook that handles clicking outside of specified elements.
The useOnClickOutside hook allows you to detect clicks outside of specific elements (referenced by refs). Supports both a single ref or an array of refs for cases where clicks on multiple elements should be ignored. This is commonly used for closing modals, dropdowns, or popovers when the user clicks elsewhere on the page.
Demo
Source Code
Copy this code into src/hooks/useOnClickOutside.ts:
import { useEffect, RefObject } from 'react';
type EventType = MouseEvent | TouchEvent | PointerEvent;
export const useOnClickOutside = <T extends HTMLElement = HTMLElement>(
refs: RefObject<T> | RefObject<T>[],
handler: (event: EventType) => void,
) => {
useEffect(() => {
const refArray = Array.isArray(refs) ? refs : [refs];
const listener = (event: EventType) => {
const target = event.target as Node;
// Do nothing if clicking inside any of the ref elements
if (refArray.some((ref) => ref.current && ref.current.contains(target))) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
document.addEventListener('pointerdown', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
document.removeEventListener('pointerdown', listener);
};
}, [refs, handler]);
};Usage
Single ref
import { useState, useRef } from 'react';
import { useOnClickOutside } from '@/hooks/useOnClickOutside';
const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
useOnClickOutside(ref, () => setIsOpen(false));
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
{isOpen && (
<div ref={ref} className="modal">
<p>Modal content</p>
</div>
)}
</div>
);
};Array of refs
import { useState, useRef } from 'react';
import { useOnClickOutside } from '@/hooks/useOnClickOutside';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const triggerRef = useRef(null);
const dropdownRef = useRef(null);
// Clicking the trigger or the dropdown panel won't close it
useOnClickOutside([triggerRef, dropdownRef], () => setIsOpen(false));
return (
<div>
<button ref={triggerRef} onClick={() => setIsOpen(!isOpen)}>
Toggle
</button>
{isOpen && (
<div ref={dropdownRef} className="dropdown-menu">
<p>Dropdown content</p>
</div>
)}
</div>
);
};API Reference
Parameters
| Name | Type | Description |
|---|---|---|
refs | RefObject<T> | RefObject<T>[] | A single ref or array of refs identifying the elements to detect outside clicks for. |
handler | (event: MouseEvent | TouchEvent | PointerEvent) => void | A callback function to execute when a click outside all the provided elements is detected. |