avatar

Le Do Nghiem

Software Engineer

  • About me
  • Books
  • Snippets
  • Blog

© 2026 Le Do Nghiem. All rights reserved.

Contact |

Back to Snippets

useClickOutside Hook

Detect clicks outside a specific element, useful for closing modals and dropdowns.

LanguageTypeScript
Last UpdatedApr 26, 2025
use-click-outside.ts
import { useEffect, RefObject } from 'react';

export function useClickOutside<T extends HTMLElement = HTMLElement>(
  ref: RefObject<T>,
  handler: (event: MouseEvent | TouchEvent) => void
): void {
  useEffect(() => {
    const listener = (event: MouseEvent | TouchEvent) => {
      const el = ref?.current;
      if (!el || el.contains(event.target as Node)) {
        return;
      }
      handler(event);
    };

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

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

Detecting clicks outside an element is a common pattern for closing modals, dropdowns, or tooltips. This hook makes it easy to handle this interaction.

import { useEffect, RefObject } from 'react';

export function useClickOutside<T extends HTMLElement = HTMLElement>(
  ref: RefObject<T>,
  handler: (event: MouseEvent | TouchEvent) => void
): void {
  useEffect(() => {
    const listener = (event: MouseEvent | TouchEvent) => {
      const el = ref?.current;
      if (!el || el.contains(event.target as Node)) {
        return;
      }
      handler(event);
    };

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

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

How It Works

  • Listens for mousedown and touchstart events on the document.
  • Checks if the click target is outside the referenced element.
  • Calls the handler if the click is outside.
  • Cleans up event listeners on unmount.

Example Usage

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

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

  return (
    <div ref={dropdownRef}>
      <button onClick={() => setIsOpen(!isOpen)}>
        Toggle Menu
      </button>
      {isOpen && (
        <div className="dropdown-menu">
          <a href="/">Home</a>
          <a href="/about">About</a>
        </div>
      )}
    </div>
  );
}

Modal Example

function Modal({ onClose, children }) {
  const modalRef = useRef<HTMLDivElement>(null);

  useClickOutside(modalRef, onClose);

  return (
    <div className="modal-overlay">
      <div ref={modalRef} className="modal-content">
        {children}
      </div>
    </div>
  );
}

Use Cases

  • Closing modals when clicking outside
  • Collapsing dropdown menus
  • Dismissing tooltips
  • Closing popover menus
  • Hiding context menus

Notes

  • Works with both mouse and touch events.
  • The handler should be memoized with useCallback to avoid unnecessary re-renders.
  • Make sure the ref is attached to the element you want to detect clicks outside of.

A clean way to handle the "click outside" pattern that's essential for modern UI components.

Previous Snippet

useLocalStorage Hook

Next Snippet

truncateString Function