-
Notifications
You must be signed in to change notification settings - Fork 0
useClickOutside
A lightweight hook that detects interactions (clicks, touches) occurring outside of one or more specified DOM elements. This is the industry-standard pattern for automatically closing dropdowns, modals, context menus, and tooltips when the user clicks away.
Closing a dropdown menu when the user clicks anywhere else on the screen.
import { useState, useRef } from "react";
import { useClickOutside } from "react-hook-lab";
function Dropdown() {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
// Instantly closes the dropdown if a click is detected outside of dropdownRef
useClickOutside(dropdownRef, () => setIsOpen(false));
return (
<div style={{ position: "relative" }}>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</button>
{isOpen && (
<div ref={dropdownRef} className="dropdown-menu">
<a href="#">Profile</a>
<a href="#">Settings</a>
<a href="#">Logout</a>
</div>
)}
</div>
);
}Listening to multiple refs simultaneously. Useful for a modal where the trigger button and the modal body are technically separate DOM elements, but clicking either shouldn't close the modal.
import { useState, useRef } from "react";
import { useClickOutside } from "react-hook-lab";
function ModalWithExternalTrigger() {
const [isOpen, setIsOpen] = useState(false);
const modalRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
// Passes an array of refs. Clicking inside ANY of these refs will be ignored.
useClickOutside([modalRef, buttonRef], () => setIsOpen(false));
return (
<div>
<button ref={buttonRef} onClick={() => setIsOpen(true)}>Open Modal</button>
{isOpen && (
<div className="modal-overlay">
<div ref={modalRef} className="modal-content">
<h2>Important Modal</h2>
<p>Clicking the overlay will close this.</p>
</div>
</div>
)}
</div>
);
}Temporarily disabling the hook for performance, or customizing the specific DOM events it listens to (e.g. tracking mouseup instead of mousedown).
import { useRef } from "react";
import { useClickOutside } from "react-hook-lab";
function AdvancedTooltip({ isVisible, onClose }: { isVisible: boolean, onClose: () => void }) {
const tooltipRef = useRef<HTMLDivElement>(null);
useClickOutside(tooltipRef, onClose, {
// Only attach the DOM listeners if the tooltip is actually visible
enabled: isVisible,
// Listen to strict pointer events instead of generic clicks
events: ["pointerup"]
});
if (!isVisible) return null;
return <div ref={tooltipRef}>Tooltip Content</div>;
}-
ref- Type:
RefObject<T> | RefObject<T>[] - Description: A single React ref, or an array of refs, pointing to the elements you want to "protect". Clicks inside these elements will NOT trigger the handler.
- Type:
-
handler- Type:
(event: Event) => void - Description: The callback function to execute when a click occurs outside the ref(s).
- Type:
-
options(Optional)- Type:
UseClickOutsideOptions - Description: Configuration object.
- Type:
export interface UseClickOutsideOptions {
enabled?: boolean; // Default: true. Disables the event listeners if false.
events?: string[]; // Default: ["mousedown", "touchstart"].
}This hook does not return any value.
When mounted, useClickOutside attaches global event listeners (by default, mousedown and touchstart) directly to the document.
Whenever one of these events fires, the hook intercepts the native DOM event. It uses the Node.contains(event.target) API to check if the exact element clicked is a child (or the element itself) of any of the provided refs. If the click target is completely independent of your refs, it fires your handler callback. It aggressively cleans up the document listeners upon unmount or when enabled is toggled to false.
Built with ❤️ by Saurav-TB-Pandey. | Report an Issue | NPM