Overview

Blueprint’s overlay system covers Popover, Tooltip, Toaster, Dialog, ContextMenu, and Drawer. All of them render through React portals, which means they escape the DOM hierarchy and land near document.body by default. The rules on this page govern when to use each primitive, how to set up the shared portal target, and how to manage z-index layering so overlays stack predictably.

Use OverlaysProvider to share one portal target across all overlay types

Without OverlaysProvider, each overlay portal mounts independently on document.body. With it, all portals share a single managed container, which makes z-index ordering deterministic and prevents the stacking context bugs that occur when a parent has transform or will-change.

import { OverlaysProvider } from "@blueprintjs/core";
 
function Root() {
  return (
    <OverlaysProvider>
      <App />
    </OverlaysProvider>
  );
}

Mount OverlaysProvider outside any element that applies a CSS transform, filter, isolation: isolate, or contain: layout. Those properties create new stacking contexts that clip position: fixed children. See css for the stacking context rules and blueprint-dialog for the dialog-specific portal guidance.

Use Popover for menus and dropdowns; Tooltip for hover hints only

Popover and Tooltip are distinct primitives with different interaction contracts.

  • Popover: triggered by click; contains interactive content (menus, forms, custom UI); the user interacts with the popover content before closing.
  • Tooltip: triggered by hover or focus; contains only informational text; closes automatically on pointer leave.
// Menu inside a Popover.
<Popover
  content={
    <Menu>
      <MenuItem icon="edit" text="Edit" onClick={handleEdit} />
      <MenuItem icon="trash" text="Delete" intent={Intent.DANGER} onClick={handleDelete} />
    </Menu>
  }
  placement="bottom-start"
>
  <Button icon="cog" text="Actions" rightIcon="chevron-down" />
</Popover>
 
// Tooltip for a non-obvious icon button.
<Tooltip content="Download as CSV">
  <Button icon="download" minimal />
</Tooltip>

Do not put interactive content in a Tooltip. The tooltip closes on pointer leave, so a user who tries to click a button inside it will lose the tooltip before reaching it. Use a Popover for any content the user needs to interact with.

Create one OverlayToaster instance per app and share it

OverlayToaster is Blueprint’s notification system. Create one instance at app startup and export it. Do not call OverlayToaster.create() in multiple places; each call creates a new toaster container, and the notifications pile up in separate DOM nodes.

// lib/toaster.ts
import { OverlayToaster } from "@blueprintjs/core";
 
export const AppToaster = OverlayToaster.createAsync({
  position: "top-right",
  maxToasts: 5,
});
 
// Anywhere in the app:
import { AppToaster } from "@/lib/toaster";
(await AppToaster).show({ message: "Saved.", intent: Intent.SUCCESS });

createAsync is preferred over create in React 18+ because it defers the DOM mount until after hydration. Always await the promise before calling .show(). Wrap the await in a helper if you call it frequently.

Order z-index layers by overlay type

Blueprint assigns z-index values through its class constants. The default order is: Popovers and Tooltips above content, Dialogs above Popovers, and Drawers at the same level as Dialogs. When mixing Blueprint overlays with other elements that set z-index, use Blueprint’s Classes constants as a reference.

LayerBlueprint constantApproximate z-index
Content(none)0
PopoverClasses.OVERLAY20
Dialog backdropClasses.OVERLAY_BACKDROP30
DialogClasses.DIALOG40
Toaster(inline style)50

Avoid setting arbitrary z-index values on elements that should compete with Blueprint overlays. If a custom dropdown or menu needs to appear above a Blueprint Popover, use Blueprint’s Popover or set the z-index based on the table above. Arbitrary large values (9999, 99999) break the ordering when a new layer needs to go above them. See css for the specificity and stacking rules.

Handle Esc dismissal consistently across nested overlays

Blueprint overlays listen for Esc at the document level and close the topmost open overlay. When a Dialog contains a Popover and both are open, pressing Esc closes the Popover first, then the Dialog on the next press. This is the correct behavior.

When a custom onKeyDown handler in a child component calls event.stopPropagation(), it prevents Blueprint from seeing the Esc key. Avoid stopPropagation on keydown events in overlay content unless you specifically need to isolate a subtree’s keyboard handling.

// Problematic: prevents Esc from closing the dialog.
<div onKeyDown={(e) => { if (e.key === "Escape") e.stopPropagation(); }}>

If a child widget needs to handle Esc for its own dismiss (a combobox closing its dropdown), call e.stopPropagation() only inside that widget and only before the overlay’s handler would fire. See accessibility for the full keyboard navigation requirements.

Avoid mixing Blueprint and Radix overlay primitives on the same surface

Blueprint and Radix each maintain an overlay stack and a focus-trap implementation. When a Blueprint Popover is open and a Radix Popover.Content is also open (for example from a shadcn component), focus-trap ownership becomes ambiguous. The two systems do not share state.

The rule is: pick one overlay system per product surface. Use Blueprint overlays for Blueprint-heavy data dashboards. Use Radix-based shadcn overlays for marketing pages and settings panels. When both must coexist on the same page, ensure they are never open simultaneously; close one before opening the other. See blueprint-dialog for the mixing rule applied to dialogs specifically.