VayuUI

useOnClickOutside

A hook that handles clicking outside of specified elements.

The useOnClickOutside hook allows you to detect clicks outside of specific elements (referenced by refs). Supports both a single ref or an array of refs for cases where clicks on multiple elements should be ignored. This is commonly used for closing modals, dropdowns, or popovers when the user clicks elsewhere on the page.

Demo

Source Code

Copy this code into src/hooks/useOnClickOutside.ts:

import { useEffect, RefObject } from 'react';

type EventType = MouseEvent | TouchEvent | PointerEvent;

export const useOnClickOutside = <T extends HTMLElement = HTMLElement>(
  refs: RefObject<T> | RefObject<T>[],
  handler: (event: EventType) => void,
) => {
  useEffect(() => {
    const refArray = Array.isArray(refs) ? refs : [refs];

    const listener = (event: EventType) => {
      const target = event.target as Node;
      // Do nothing if clicking inside any of the ref elements
      if (refArray.some((ref) => ref.current && ref.current.contains(target))) {
        return;
      }
      handler(event);
    };

    document.addEventListener('mousedown', listener);
    document.addEventListener('touchstart', listener);
    document.addEventListener('pointerdown', listener);

    return () => {
      document.removeEventListener('mousedown', listener);
      document.removeEventListener('touchstart', listener);
      document.removeEventListener('pointerdown', listener);
    };
  }, [refs, handler]);
};

Usage

Single ref

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

const Modal = () => {
  const [isOpen, setIsOpen] = useState(false);
  const ref = useRef(null);

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

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>

      {isOpen && (
        <div ref={ref} className="modal">
          <p>Modal content</p>
        </div>
      )}
    </div>
  );
};

Array of refs

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

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

  // Clicking the trigger or the dropdown panel won't close it
  useOnClickOutside([triggerRef, dropdownRef], () => setIsOpen(false));

  return (
    <div>
      <button ref={triggerRef} onClick={() => setIsOpen(!isOpen)}>
        Toggle
      </button>

      {isOpen && (
        <div ref={dropdownRef} className="dropdown-menu">
          <p>Dropdown content</p>
        </div>
      )}
    </div>
  );
};

API Reference

Parameters

NameTypeDescription
refsRefObject<T> | RefObject<T>[]A single ref or array of refs identifying the elements to detect outside clicks for.
handler(event: MouseEvent | TouchEvent | PointerEvent) => voidA callback function to execute when a click outside all the provided elements is detected.

On this page