← Back to all hooks

useOnClickOutside

Detect clicks outside of elements. Perfect for closing dropdowns, popups, and modals.

Live Demo

📊 Click Statistics

0 clicks detected

Click anywhere in the gray area

📝 Event Log

No events yet. Try opening the menus!

💡 Try This:

  • Open a menu and click outside to close it
  • Click inside the menu - it stays open
  • Open both menus and close them independently

Code Examples

Basic Dropdown

import { useOnClickOutside } from "@uiblock/hooks";
import { useState, useRef } from "react";

function Dropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef(null);

  useOnClickOutside(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef}>
      <button onClick={() => setIsOpen(!isOpen)}>
        Toggle Dropdown
      </button>
      {isOpen && (
        <div className="dropdown-menu">
          <div>Option 1</div>
          <div>Option 2</div>
          <div>Option 3</div>
        </div>
      )}
    </div>
  );
}

Multiple Elements

import { useOnClickOutside } from "@uiblock/hooks";
import { useRef } from "react";

function Component() {
  const buttonRef = useRef(null);
  const menuRef = useRef(null);

  // Close when clicking outside both elements
  useOnClickOutside([buttonRef, menuRef], () => {
    console.log('Clicked outside!');
  });

  return (
    <>
      <button ref={buttonRef}>Button</button>
      <div ref={menuRef}>Menu</div>
    </>
  );
}

With Options

import { useOnClickOutside } from "@uiblock/hooks";

function Modal({ isOpen, onClose }) {
  const modalRef = useRef(null);

  useOnClickOutside(modalRef, onClose, {
    enabled: isOpen,
    eventTypes: ['mousedown', 'touchstart']
  });

  return isOpen ? <div ref={modalRef}>Modal</div> : null;
}

How It Works

Here's the implementation:

import { useEffect, useRef } from 'react'

export function useOnClickOutside(ref, handler, options = {}) {
  const { enabled = true, eventTypes = ['mousedown', 'touchstart'] } = options
  const savedHandler = useRef(handler)

  useEffect(() => {
    savedHandler.current = handler
  }, [handler])

  useEffect(() => {
    if (!enabled) return

    const listener = (event) => {
      const refs = Array.isArray(ref) ? ref : [ref]

      // Check if click is inside any of the refs
      const isInside = refs.some(r => {
        const element = r.current
        if (!element) return false

        const target = event.target
        return element.contains(target)
      })

      if (!isInside) {
        savedHandler.current(event)
      }
    }

    // Add listeners for all event types
    eventTypes.forEach(eventType => {
      document.addEventListener(eventType, listener)
    })

    return () => {
      eventTypes.forEach(eventType => {
        document.removeEventListener(eventType, listener)
      })
    }
  }, [ref, enabled, eventTypes])
}

Key Features:

  • Supports single or multiple refs
  • Configurable event types (mouse and touch)
  • Stable handler reference with useRef
  • Automatic cleanup on unmount

API Reference

Parameters

ref: RefObject | RefObject[]

Element ref(s) to detect outside clicks

handler: (event) => void

Callback when click outside is detected

options?: UseOnClickOutsideOptions

enabled, eventTypes configuration

💡 Use Cases

  • Dropdown menus
  • Context menus
  • Popover components
  • Modal dialogs
  • Autocomplete suggestions
  • Date pickers