PrimeReact 11.1.0 landed on 5 August 2026. It is a small release and most of it is a single fix, in the overlay components, for a bug that comes out of portals and event ordering. That combination catches people out often enough that the fix is most of this post.
npm install [email protected]
It needs React 19, same as 11.0.
Clicking inside an overlay could close it
Select, AutoComplete, DatePicker, Menu, ContextMenu and Popover all render their
panel through a portal. The panel is a child of document.body, not of the
component that owns it.
Those components also dismiss on outside clicks, which means a listener on
document. In usePopover that listener is bound on pointerdown with
useEventListener, whose target defaults to document. It asks whether the
event landed inside the overlay, and closes it when the answer is no.
The containment check is where the portal causes trouble. In the DOM, a portaled panel is not inside the component’s element, so checking the anchor and the positioner for the click target says the click was outside. React hides this in its own synthetic event system, where events bubble through the React tree rather than the DOM tree, and a synthetic handler on the panel does fire.
The synthetic handler is not much help here, because React attaches its
listeners at the root container. By the time the synthetic handler on the panel
runs, the document listener has already fired and already decided to close, so
the panel closes underneath the click that was meant for it.
The case that made this visible was a Select opened inside a Popover. The Select’s own panel is portaled to the body, so as far as the Popover was concerned a click on an option happened somewhere else entirely, and the Popover dismissed itself along with the Select inside it.
How useOverlayClickEmitter closes the race
11.1.0 adds useOverlayClickEmitter in @primereact/core/utils, used by all
six popup components. Its own documentation describes the problem exactly:
Bound natively in the capture phase: a React synthetic handler on a portaled popup runs after the
document-level listener that consumers (seeusePopover) dismiss on, and would lose that race.
Two decisions in that sentence carry the fix. The listener is native rather than a React one, so it runs in the same system as the dismiss logic, and it binds in the capture phase on the popup element itself, which puts the overlay’s announcement ahead of the bubble-phase listener at document level.
The signature is small:
useOverlayClickEmitter(
popupElement: HTMLElement | null | undefined,
getTarget: () => Element | null | undefined,
): void
The popup element is tracked in state rather than a ref, so the effect re-runs
when the panel mounts. getTarget reports which element owns the overlay, and
with that the dismiss check on the owner can recognise an interaction that
happened in a panel somewhere else in the DOM. The emitter listens for
pointerdown only, and the reason for that is in the source: a second emit per
interaction leaves an unconsumed self-click flag behind, and that flag then
swallows the next genuine outside click. The event it publishes on
OverlayEventBus is overlay-click, and usePopover consumes it by setting a
self-click flag that its own dismiss handler reads and clears.
That clearing is the second half of the fix. The flag describes the current interaction and nothing more, so it is read and reset on every interaction now rather than only when it was set. If it stays set, the next outside click reads a flag left over from an earlier interaction and does nothing, which is how a stale flag used to turn into a dismiss that never happened.
Ownership across the portal
The emitter alone is not enough for overlays nested inside overlays. For that
case 11.1.0 adds overlayOwner, which links a portaled popup back to the anchor
that opened it, using a data-overlay-owner attribute plus a property holding
the anchor element itself. It holds an element reference and not an id, so
nothing needs generating and the link cannot point at something that has gone
away.
usePopover then walks that chain. Its new ownsNode helper checks the
positioner and the anchor first, and when the node is in neither, climbs the
owner chain hop by hop to see whether it eventually lands back inside this
overlay. Both the outside-click check and the focus-out check go through it, so
“focus moved into a nested overlay of mine” and “focus left entirely” are two
different answers now instead of one.
If you have built your own portaled overlay on top of PrimeReact’s primitives
and you have been fighting the same race, useOverlayClickEmitter and
overlayOwner are the two pieces to reach for.
Sidebar navigation, locales and Escape handling
Sidebar got tree-style keyboard navigation, which is a bigger change than the
name suggests. The menu handles arrow keys, Home and End, and left and right
collapse and expand a branch. New prop getters and types come with it, so have a
look at those if you drive Sidebar from headless.
PrimeReactProvider takes a locales prop, so several languages can be
registered in one place instead of swapping a single locale object at runtime.
There is also a new useEscapeListener hook. Escape handling used to be one
listener per component and is now a dismiss stack. The topmost layer wins, and a
layer with closeOnEscape: false swallows the key instead of passing it down,
so one press on a dialog with a popover open behind it closes the dialog only.
Dialog and Popover both went through the new hook.
Two fixes in core: the defaults config no longer drops props a component does
not declare, and locale registration no longer depends on import order or misses
on first render.
If you import from @primereact/core/utils directly, note that
ConnectedOverlayScrollHandler is gone. It was deleted along with its export in
the same commit as the overlay fix.
If you are on 11.0 and any of those six components sit inside a portal-heavy layout, this is the upgrade to take.