DOM

useClickOutside

Runs a callback on outside click.

Demo

menu
closed
last event
nothing yet

Open the menu, then click anywhere outside it.

Usage

use-click-outside.tsxtsx
import { useRef, useState } from "react";
import { useClickOutside } from "hookli";

export function Demo() {
  const menuRef = useRef<HTMLDivElement>(null);
  const [open, setOpen] = useState(false);

  useClickOutside(menuRef, () => setOpen(false));

  return (
    <div ref={menuRef}>
      <button onClick={() => setOpen((prev) => !prev)}>Actions</button>
      {open && (
        <ul role="menu">
          <li>Rename</li>
          <li>Duplicate</li>
        </ul>
      )}
    </div>
  );
}

API

useClickOutside.d.tsts
useClickOutside<T extends HTMLElement>(ref: RefObject<T>, callback: () => void): void

Parameters

NameTypeDefaultDescription
refRefObject<T>Ref attached to the element that counts as inside — clicks within it (or its children) never fire the callback.
callback() => voidCalled on every mousedown outside the ref'd element — even while your UI is closed, so guard inside the callback if needed.

Returns

This hook returns nothing.

Hook

The implementation, straight from hookli. Copy it, or install the package.

src/hooks/useClickOutside.hook.tstsx
import { Ref, useEffect } from "react";

type ClickOutsideCallback = () => void;

export const useClickOutside = <T extends HTMLElement>(
  ref: Ref<T>,
  callback: ClickOutsideCallback,
) => {
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        ref &&
        "current" in ref &&
        !ref.current?.contains(event.target as Node)
      ) {
        callback();
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref, callback]);
};

View source on GitHub

Related hooks