UnflowUI
Components

ClickAwayEvent

A headless wrapper that detects clicks outside its subtree and fires a callback. The composable primitive used by Popper and Dropdown.

Overview

ClickAwayEvent is a utility component that attaches a document-level click listener and calls onClickAway when a click occurs outside its children. It renders as a <div> wrapper with no visual styling.

It powers the onClickAway behavior of Popper and Dropdown, but can also be used standalone when you need click-outside detection on a custom element.

Import

import { ClickAwayEvent } from '@unflow/ui/components/ClickAwayEvent';

Props

PropTypeDefaultDescription
onClickAway() => voidCalled when a click occurs outside the wrapper
listenbooleantrueEnables/disables the listener without DOM changes
ignoreElement | nullAn element to exclude from click-away detection

All standard HTMLDivElement props are accepted and forwarded.

Examples

Basic

<ClickAwayEvent onClickAway={() => setOpen(false)}>
  <div className="p-4 bg-white shadow rounded">
    Dropdown content
  </div>
</ClickAwayEvent>

Conditionally armed

Use listen={false} to disable the listener without unmounting the component. This preserves the DOM node and any internal state:

<ClickAwayEvent listen={isOpen} onClickAway={handleClose}>
  <Menu />
</ClickAwayEvent>

Ignoring the trigger element

A common pattern: the button that opens a dropdown should not trigger close when clicked. Pass the button element to ignore:

const buttonRef = useRef<HTMLButtonElement>(null);

<button ref={buttonRef} onClick={toggle}>Open</button>

<ClickAwayEvent
  listen={isOpen}
  onClickAway={close}
  ignore={buttonRef.current}
>
  <Dropdown />
</ClickAwayEvent>

Implementation notes

Capture phase, not bubble. The click listener is attached with { capture: true }. This makes it fire before React's synthetic event system, which prevents a race condition where the same click that opens a dropdown (on the trigger button) is also immediately caught as a click-away and closes it.

listen vs. unmounting. ClickAwayEvent does not add or remove the listener by toggling the component in/out of the DOM — doing so would cause flickering and state loss in complex dropdowns. Instead, the listen prop conditionally registers and deregisters the addEventListener call, keeping the wrapper in the tree at all times.

Accessibility

ClickAwayEvent is purely behavioral and has no ARIA attributes. The element renders as a <div> — if this introduces an unwanted layout wrapper, you can override its display with className="contents".