useOutsideClick
A custom React hook that detects clicks outside of a specified element — perfect for closing dropdowns, modals, and popovers.Copy Markdown
Overview
useOutsideClick listens for mousedown and touchstart events on the document and fires a handler whenever the user interacts outside the referenced DOM node. It automatically cleans up its event listeners on unmount, preventing memory leaks.
Commonly used for:
- Closing dropdown menus
- Dismissing modals and drawers
- Collapsing search results
- Hiding tooltip / popover panels
Installation
Automatic (CLI)
The fastest way — pull the hook directly into your project.
Before using the @nurav-ui alias, add this to your components.json:
{
"registries": {
"@nurav-ui": "https://nurav-ui.vercel.app/r"
}
}Or skip alias setup entirely and use the direct URL below.
npx shadcn@latest add @nurav-ui/use-outside-clickAlternatively, install via direct URL (no components.json changes needed):
npx shadcn@latest add https://nurav-ui.vercel.app/r/use-outside-click.jsonManual Setup
Create the hook file
Copy the source into hooks/use-outside-click.ts:
import { useEffect, RefObject } from "react";
export function useOutsideClick(
ref: RefObject<HTMLElement | null>,
handler: (event?: MouseEvent | TouchEvent) => void,
) {
useEffect(() => {
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (!ref.current || ref.current.contains(event.target as Node)) {
return;
}
handler(event);
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("touchstart", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("touchstart", handleClickOutside);
};
}, [ref, handler]);
}Import and use
Import the hook anywhere in your component tree:
import { useRef } from "react";
import { useOutsideClick } from "@/hooks/use-outside-click";Usage
Basic Example
import { useRef } from 'react';
import { useOutsideClick } from '@/hooks/use-outside-click';
export default function MyComponent() {
const containerRef = useRef<HTMLDivElement>(null);
useOutsideClick(containerRef, () => {
console.log('Clicked outside!');
});
return (
<div ref={containerRef} className="p-4 border rounded-lg">
Click inside me — nothing happens.
Click outside me — check the console.
</div>
);
}Dropdown Example
A real-world pattern: a dropdown that closes when the user clicks outside it.
import { useRef, useState, useCallback } from 'react';
import { useOutsideClick } from '@/hooks/use-outside-click';
export default function Dropdown() {
const [open, setOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const handleClose = useCallback(() => setOpen(false), []);
useOutsideClick(dropdownRef, handleClose);
return (
<div ref={dropdownRef} className="relative inline-block">
<button
onClick={() => setOpen((prev) => !prev)}
className="px-4 py-2 rounded-lg border"
>
Toggle Menu
</button>
{open && (
<ul className="absolute mt-2 w-48 rounded-lg border bg-background shadow-lg">
<li className="px-4 py-2 hover:bg-foreground/5 cursor-pointer">Profile</li>
<li className="px-4 py-2 hover:bg-foreground/5 cursor-pointer">Settings</li>
<li className="px-4 py-2 hover:bg-foreground/5 cursor-pointer">Sign out</li>
</ul>
)}
</div>
);
}API Reference
Parameters
Return Value
This hook returns void — it operates purely via side effects.
Stable handler tip: Wrap your handler in useCallback to prevent the effect from re-running on every render when the handler is defined inline.
const handleClose = useCallback(() => setOpen(false), []);
useOutsideClick(ref, handleClose);The hook listens on mousedown (not click) to ensure the handler fires before any onClick on inner elements — preventing race conditions with controlled state updates.