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

More React tasks

Back to the full React Custom Hooks cheat sheet