Hooks
useClickOutside
BetaSince 0.1.0
Calls a function when the reader clicks outside an element, for closing a menu or a panel.
import { useClickOutside } from "@anantya/uix";Overview
useClickOutside calls a function when the reader clicks or taps outside the elements you give it. It is for closing popups, as Popover and Dropdown Menu will. In product code, use it for state that ends on a click elsewhere, such as a selection.
Building block
For a popup, use Popover or Dropdown Menu, coming in v1, which close on a click outside already.
Missing a component? Ask for it instead of building your own.
Signature
function useClickOutside(
refs: RefObject<Element | null> | readonly RefObject<Element | null>[],
handler: (event: PointerEvent) => void,
options?: UseClickOutsideOptions,
): void;
interface UseClickOutsideOptions {
enabled?: boolean;
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
refs | RefObject<Element | null> or an array of them | - | The elements that count as inside, such as the popup and the button that opens it. |
handler | (event: PointerEvent) => void | - | Called with the pointerup event of a click outside. |
options.enabled | boolean | true | Listens while true, such as while the popup is open. |
Returns
Nothing. It listens while enabled is true and stops when it turns false or the component unmounts.
Example
Select some invoices, then click anywhere outside them to clear the selection.
0 selected
Code
"use client";
import { Button, useClickOutside } from "@anantya/uix";
import { useRef, useState } from "react";
const invoices = ["INV-2026-0140", "INV-2026-0141", "INV-2026-0142"];
export default function ClearSelection() {
const [selected, setSelected] = useState<string[]>([]);
const list = useRef<HTMLDivElement>(null);
useClickOutside(list, () => setSelected([]), { enabled: selected.length > 0 });
const toggle = (id: string) =>
setSelected((current) =>
current.includes(id) ? current.filter((other) => other !== id) : [...current, id],
);
return (
<div className="flex flex-col items-center gap-3 text-sm">
<div ref={list} className="flex flex-wrap justify-center gap-2">
{invoices.map((id) => (
<Button
key={id}
size="sm"
variant={selected.includes(id) ? "primary" : "secondary"}
aria-pressed={selected.includes(id)}
onClick={() => toggle(id)}
>
{id}
</Button>
))}
</div>
<p className="text-muted-foreground">{selected.length} selected</p>
</div>
);
}Notes
- Pass the ref of every element that counts as inside, such as a button that starts the state too. Otherwise a click on it ends the state and starts it again
- Pair it with useEscapeKey, so keyboard users can end the state too
- A click counts only when it both starts and ends outside, so selecting text that ends outside closes nothing, and neither does a touch that scrolls the page
- When several are enabled, only the one enabled last listens: a click outside a nested popup closes only that popup
- It listens before other handlers, so
stopPropagation()elsewhere can't block it - It does nothing on the server, or while no ref has an element
Changelog
Every change to useClickOutside since 0.1.0 is in the release notes.