outside-click-hook
v0.1.3
Published
A tiny, zero-dependency React hook for detecting clicks outside one or more elements.
Maintainers
Readme
outside-click-hook
A tiny, zero-dependency, TypeScript-first React hook for detecting interactions outside one or more elements.
Installation
npm install outside-click-hookpnpm add outside-click-hookyarn add outside-click-hookQuick Start
import { useClickOutside } from "outside-click-hook";
export function Menu({ onClose }: { onClose: () => void }) {
const ref = useClickOutside<HTMLDivElement>(() => {
onClose();
});
return <div ref={ref}>Menu content</div>;
}API
Create and return one ref
const ref = useClickOutside(() => {
console.log("Outside click");
});Configure behavior
const ref = useClickOutside(onClose, {
enabled: true,
event: "pointerdown",
});Use existing refs
const modalRef = useRef<HTMLDivElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
useClickOutside([modalRef, dropdownRef], onClose);Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| enabled | boolean | true | Enables or disables all listeners. |
| event | "click" \| "mousedown" \| "mouseup" \| "pointerdown" | "pointerdown" | Document event used for outside interactions. |
| escapeKey | boolean | true | Calls the handler when Escape is pressed. |
| ignoreScrollbar | boolean | true | Ignores clicks on the browser scrollbar. |
| ignore | Array<React.RefObject<HTMLElement \| null>> | [] | Refs that should not trigger the outside handler. |
Examples
Modal
import { useClickOutside } from "outside-click-hook";
function Modal({ onClose }: { onClose: () => void }) {
const ref = useClickOutside<HTMLDivElement>(onClose, { escapeKey: true });
return (
<div role="dialog" aria-modal="true" ref={ref}>
Modal content
</div>
);
}Dropdown with ignored trigger
const buttonRef = useRef<HTMLButtonElement>(null);
const menuRef = useClickOutside<HTMLDivElement>(onClose, {
ignore: [buttonRef],
});Popover with multiple refs
useClickOutside([triggerRef, popoverRef], onClose, {
enabled: open,
event: "pointerdown",
});More complete examples live in examples.
TypeScript
The package is written in TypeScript and ships generated declaration files. Pass the element type as a generic when using the returned ref:
const ref = useClickOutside<HTMLButtonElement>(onOutside);The handler receives the native event that triggered it:
const ref = useClickOutside<HTMLDivElement>((event) => {
event.type;
});Browser Compatibility
pointerdown is the default because it works well across mouse, pen, and touch input in modern browsers. You can switch to click, mousedown, or mouseup when a specific interaction model needs it.
The hook is SSR safe: it does not access window or document during render.
FAQ
Does this package support React 19?
Yes. React is a peer dependency and the supported range supports React 18 and newer.
Does the hook work with portals?
Yes. The listener runs on document in the capture phase and uses composedPath() when available, which helps with portals and shadow DOM boundaries.
Does it have runtime dependencies?
No. The package has zero runtime dependencies.
Why is Escape enabled by default?
Escape is a common close gesture for modals, popovers, context menus, and dropdowns. Disable it with escapeKey: false.
Contributing
- Install dependencies with
npm install. - Run
npm run lint. - Run
npm run typecheck. - Run
npm test. - Run
npm run build.
Please add or update tests for behavior changes.
License
MIT (c) outside-click-hook contributors
