---
title: "Blueprint: Overlays"
slug: "blueprint-overlays"
category: "frontend"
tags: ["frontend", "blueprint", "react", "popover", "tooltip", "toaster", "z-index", "overlay"]
status: "stable"
last_updated: 2026-05-14
summary: "Blueprint overlays: use Popover2 for menus, Tooltip2 for hints, OverlayToaster for notifications, and control z-index via Classes.OVERLAY_BACKDROP."
related: ["[[frontend/blueprint]]", "[[frontend/blueprint-dialog]]", "[[frontend/blueprint-theming]]", "[[frontend/blueprint-table]]", "[[frontend/react]]", "[[frontend/accessibility]]", "[[frontend/css]]"]
---

## 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`.

```tsx
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 [[frontend/css]] for the stacking context rules and [[frontend/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.

```tsx
// 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.

```tsx
// 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.

| Layer | Blueprint constant | Approximate z-index |
|---|---|---|
| Content | (none) | 0 |
| Popover | `Classes.OVERLAY` | 20 |
| Dialog backdrop | `Classes.OVERLAY_BACKDROP` | 30 |
| Dialog | `Classes.DIALOG` | 40 |
| 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 [[frontend/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.

```tsx
// 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 [[frontend/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 [[frontend/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 [[frontend/blueprint-dialog]] for the mixing rule applied to dialogs specifically.

## Related

- [[frontend/blueprint]]
- [[frontend/blueprint-dialog]]
- [[frontend/blueprint-theming]]
- [[frontend/blueprint-table]]
- [[frontend/react]]
- [[frontend/accessibility]]
- [[frontend/css]]
