useClickOutside & useEventListener in React

From the React Custom Hooks cheat sheet · Interaction & Lifecycle Hooks · verified Jul 2026

useClickOutside & useEventListener

Dismiss menus/popovers and attach typed event listeners safely

jsx
import { useEffect, useRef, RefObject } from 'react'

function useClickOutside<T extends HTMLElement>(
  ref: RefObject<T>,
  onOutside: (e: MouseEvent | TouchEvent) => void,
) {
  useEffect(() => {
    const handler = (e: MouseEvent | TouchEvent) => {
      const el = ref.current
      if (!el || el.contains(e.target as Node)) return
      onOutside(e)
    }
    document.addEventListener('mousedown', handler)
    document.addEventListener('touchstart', handler)
    return () => {
      document.removeEventListener('mousedown', handler)
      document.removeEventListener('touchstart', handler)
    }
  }, [ref, onOutside])
}

// Usage
function Dropdown() {
  const ref = useRef<HTMLDivElement>(null)
  const [open, setOpen] = useState(false)
  useClickOutside(ref, () => setOpen(false))
  return <div ref={ref}>{open && <Menu />}</div>
}
💡 Use mousedown (not click) - fires before focus/blur and feels snappier
⚡ The handlerRef pattern avoids re-binding listeners every render
📌 touchstart needs { passive: true } to avoid scroll-blocking warnings
🎯 useEventListener is the typed, cleanup-safe primitive for any DOM event
eventsdomui

Continue with React Custom Hooks

Save the full cheat sheet or work through every related task.

More React tasks

Back to the full React Custom Hooks cheat sheet