Join our Discord Community

React useOnClickOutside Hook

React useOnClickOutside hook for outside click detection. Handle modal dismissal and dropdown closing with multiple element support using TypeScript for Next.js.

Click Outside Not Working?

Join our Discord community for help from other developers.


Ever tried to detect clicks outside a modal or dropdown and ended up with document.addEventListener hell, event delegation nightmares, or broken portal interactions? You know the drill—manually checking event.target.contains(), forgetting to removeEventListener, dealing with React boundaries and escape key handling. This free open source React useOnClickOutside custom hook handles all that outside-click complexity so you can focus on building clean UI interactions instead of debugging event propagation edge cases in your React applications.

useOnClickOutside showcase

Smart outside-click detection with multiple element support:

Loading component...

This free open source React hook simplifies outside-click detection with TypeScript support for modern JavaScript applications. Whether you're building modals, dropdowns, or context menus in your Next.js projects, this React hook keeps your dismissal logic clean and reliable.

Installation

npx shadcn@latest add https://www.shadcn.io/registry/use-on-click-outside.json
npx shadcn@latest add https://www.shadcn.io/registry/use-on-click-outside.json
pnpm dlx shadcn@latest add https://www.shadcn.io/registry/use-on-click-outside.json
bunx shadcn@latest add https://www.shadcn.io/registry/use-on-click-outside.json

Why most outside-click implementations suck

Look, you could keep adding document click listeners and manually checking contains() methods. But then you hit the outside-click complexity—React portal boundaries, event timing issues, memory leaks from forgotten cleanup, escape key coordination in React applications.

Most developers add raw document listeners without proper cleanup, causing memory leaks when components unmount in TypeScript components. Or they forget about React portals and get confused when clicks inside modals unexpectedly trigger close handlers. Some use event.target.contains() without accounting for shadow DOM or dynamically rendered content in Next.js projects.

This React hook uses optimized event delegation under the hood, with automatic React boundary detection and proper cleanup in JavaScript applications. The browser handles all the event propagation, plus you get support for multiple elements and different event types in one call.

Plus it handles all the edge cases—portal boundaries, dynamically rendered content, touch vs mouse events in React development. No more scattered document listeners or broken modal interactions.

This free open source React hook manages outside-click state while you focus on building features. Whether you're creating React applications, Next.js interfaces, or TypeScript components, reliable dismissal detection keeps your JavaScript development smooth.

Features

  • Multiple element support monitoring single ref or array of refs with unified handling in React applications
  • Flexible event types supporting mouse, touch, and focus events for comprehensive interaction in TypeScript components
  • React boundary aware works correctly with portals and dynamically rendered content in Next.js projects
  • Performance optimized using efficient event delegation with automatic cleanup in JavaScript development
  • TypeScript generics with type-safe element references and event handling for React frameworks
  • Event listener options supporting passive listeners and capture phase control in modern applications
  • Free open source designed for modern React development workflows

When you'll actually use this

Real talk—this isn't for simple toggle states in React applications. CSS :focus-within handles many focus cases perfectly. But when you need precise outside-click detection for complex UI patterns or multi-element coordination, this React hook delivers in Next.js projects.

Perfect for:

  • Modal dialogs - Close modals when clicking outside with proper escape key handling built with TypeScript
  • Dropdown menus - Hide dropdowns on outside clicks with keyboard navigation support using React patterns
  • Context menus - Dismiss right-click menus with consistent interaction patterns in JavaScript applications
  • Tooltip management - Auto-hide tooltips when focus moves elsewhere or clicks occur in React components
  • Sidebar navigation - Collapse mobile sidebars on outside interactions in Next.js applications
  • Multi-step forms - Auto-save or validate when user clicks away from form sections using TypeScript safety

API Reference

useOnClickOutside

useOnClickOutside<T extends HTMLElement = HTMLElement>(
  ref: RefObject<T> | RefObject<T>[],
  handler: (event: Event) => void,
  eventType?: EventType,
  eventListenerOptions?: AddEventListenerOptions
): void
ParameterTypeDefaultDescription
refRefObject<T> | RefObject<T>[]requiredSingle ref or array of refs to monitor for outside clicks
handler(event: Event) => voidrequiredCallback fired when clicking outside element(s)
eventTypeEventType'mousedown'Event type to listen for on document
eventListenerOptionsAddEventListenerOptions{}Native event listener options for fine-tuned control

Event Types

TypeDescriptionUse Case
'mousedown'Mouse button pressed (default)Standard click detection for most UI patterns
'mouseup'Mouse button releasedAlternative timing for click detection
'touchstart'Touch interaction beginsMobile-optimized touch detection
'touchend'Touch interaction endsAlternative mobile touch timing
'focusin'Element receives focusFocus-based dismissal for accessibility
'focusout'Element loses focusFocus loss detection for form validation

Event Listener Options

OptionTypeDescription
passivebooleanPassive event listener for improved scroll performance
capturebooleanCapture events in capture phase before bubbling
oncebooleanRemove listener after first trigger for one-time detection

Common gotchas

React portals need special handling: The React hook automatically handles portal boundaries, but make sure your portal roots are properly managed in React applications. Outside clicks on portal content won't trigger the handler when the portal element is included in the refs array.

Event timing matters for complex interactions: Use 'mousedown' for immediate response, 'mouseup' for drag-and-drop compatibility in TypeScript components. Touch events ('touchstart', 'touchend') provide better mobile experience but may conflict with scroll gestures.

Multiple refs require careful coordination: When using ref arrays, ensure all related elements are included to prevent unwanted dismissals in Next.js projects. Missing a trigger button or input field in the array will cause the handler to fire when clicking those elements.

Event listener cleanup is automatic: The React hook manages listeners internally, but be mindful of creating many concurrent outside-click detectors in JavaScript applications. Each instance adds a document-level listener that persists until component unmount.

Handler function stability: Ensure your handler function is stable across renders in React frameworks. Use useCallback to prevent unnecessary re-registrations of event listeners.

Portal content coordination: When working with modals or dropdowns that render in portals, include both trigger and content elements in the refs array to prevent unexpected dismissals in TypeScript projects.

Questions you might have