useClickOutside

useClickOutside

The useClickOutside hook is a custom React hook designed to detect interactions outside a specified HTML element. It is particularly useful for implementing dropdowns, modals, or any UI component that requires closing or reacting to outside clicks or interactions.

Props

  • handler (event: Event) => void: The callback function that gets triggered when an interaction outside the referenced element is detected..
  • events EventType[] (Optional): An array of event types to listen for. Defaults to ['mousedown']. Supported values: "mousedown", "mouseup", "touchstart", "touchend", "focusin", "focusout"
  • listenCapturing boolean (Optional): Indicates whether event listeners should use the capturing phase. Defaults to true.

Returns

  • RefObject<T> A React ref object that should be attached to the target HTML element you want to monitor.

The Hook

useClickOutside.ts
import { useIsomorphicEffect } from "hooks/useIsomorphicEffect";
import { RefObject, useRef } from "react";
 
type EventType =
  | "mousedown"
  | "mouseup"
  | "touchstart"
  | "touchend"
  | "focusin"
  | "focusout";
 
export function useClickOutside<T extends HTMLElement = HTMLElement>(
  handler: (event: Event) => void,
  events: EventType[] = ["mousedown"],
  listenCapturing: boolean = true
): RefObject<T> {
  const ref = useRef<T>(null);
 
  useIsomorphicEffect(() => {
    function handleClickOutside(event: Event) {
      if (typeof window === "undefined") return;
 
      if (ref.current && !ref.current.contains(event.target as Node)) {
        handler(event);
      }
    }
 
    events.forEach((eventType) => {
      document.addEventListener(eventType, handleClickOutside, listenCapturing);
    });
 
    return () => {
      events.forEach((eventType) => {
        document.removeEventListener(
          eventType,
          handleClickOutside,
          listenCapturing
        );
      });
    };
  }, [handler, events, listenCapturing]);
 
  return ref;
}

Usage

UseClickOutsideDemo.tsx
import React, { useState } from "react";
import { useClickOutside } from "hooks/useClickOutside";
 
function Dropdown() {
  const [isOpen, setIsOpen] = useState(false);
 
  const ref = useClickOutside<HTMLDivElement>(() => {
    setIsOpen(false); // Close the dropdown when clicking outside
  });
 
  return (
    <div>
      <button onClick={() => setIsOpen((prev) => !prev)}>
        Toggle Dropdown
      </button>
      {isOpen && (
        <div ref={ref} style={{ border: "1px solid black", padding: "10px" }}>
          <p>Dropdown Content</p>
        </div>
      )}
    </div>
  );
}