# Popover

> Popover: anatomy, keyboard behavior, state, and theming in Askr.

Source: [https://askrjs.com/docs/components/popover](https://askrjs.com/docs/components/popover)

Status: stable. Packages: @askrjs/ui/popover, @askrjs/themes/popover.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
import { Button, Popover, PopoverContent, PopoverTrigger } from '@askrjs/themes/components';

<Popover>
  <PopoverTrigger asChild><Button variant="outline">Filters</Button></PopoverTrigger>
  <PopoverContent><ProjectFilters /></PopoverContent>
</Popover>
```

## Published props

Generated from the TypeScript declarations shipped by the installed package.

### `PopoverCloseAsChildProps`

Import from `@askrjs/ui/popover`.

- `asChild: true;`
- `children: JSXElement;`
- `disabled?: boolean | undefined;`
- `onPress?: ((event: PressEvent) => void) | undefined;`
- `ref?: Ref<Element>;`
- `type?: undefined;`

### `PopoverCloseProps`

Import from `@askrjs/ui/popover`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `disabled?: boolean | undefined;`
- `onPress?: ((event: PressEvent) => void) | undefined;`
- `ref?: Ref<HTMLButtonElement>;`
- `type?: "button" | "submit" | "reset" | undefined;`

### `PopoverContentAsChildProps`

Import from `@askrjs/ui/popover`.

- `align?: OverlayAlign | undefined;`
- `asChild: true;`
- `children: JSXElement;`
- `forceMount?: boolean | undefined;`
- `ref?: Ref<Element>;`
- `side?: OverlaySide | undefined;`
- `sideOffset?: number | undefined;`
- `width?: PopoverContentWidth | undefined;`

### `PopoverContentOwnProps`

Import from `@askrjs/ui/popover`.

- `align?: OverlayAlign | undefined;`
- `forceMount?: boolean | undefined;`
- `side?: OverlaySide | undefined;`
- `sideOffset?: number | undefined;`
- `width?: PopoverContentWidth | undefined;`

### `PopoverContentProps`

Import from `@askrjs/ui/popover`.

- `align?: OverlayAlign | undefined;`
- `asChild?: false | undefined;`
- `children?: unknown;`
- `forceMount?: boolean | undefined;`
- `ref?: Ref<HTMLDivElement>;`
- `side?: OverlaySide | undefined;`
- `sideOffset?: number | undefined;`
- `width?: PopoverContentWidth | undefined;`

### `PopoverOwnProps`

Import from `@askrjs/ui/popover`.

- `children?: unknown;`
- `defaultOpen?: boolean | undefined;`
- `id?: string | undefined;`
- `onOpenChange?: ((open: boolean) => void) | undefined;`
- `open?: boolean | undefined;`

### `PopoverPortalProps`

Import from `@askrjs/ui/popover`.

- `children?: unknown;`

### `PopoverProps`

Import from `@askrjs/ui/popover`.

- `children?: unknown;`
- `defaultOpen?: boolean | undefined;`
- `id?: string | undefined;`
- `onOpenChange?: ((open: boolean) => void) | undefined;`
- `open?: boolean | undefined;`

### `PopoverTriggerAsChildProps`

Import from `@askrjs/ui/popover`.

- `asChild: true;`
- `children: JSXElement;`
- `disabled?: boolean | undefined;`
- `onPress?: ((event: PressEvent) => void) | undefined;`
- `ref?: Ref<Element>;`
- `type?: undefined;`

### `PopoverTriggerProps`

Import from `@askrjs/ui/popover`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `disabled?: boolean | undefined;`
- `onPress?: ((event: PressEvent) => void) | undefined;`
- `ref?: Ref<HTMLButtonElement>;`
- `type?: "button" | "submit" | "reset" | undefined;`

## Purpose

Popover is a non-modal floating panel anchored to a trigger element — think a filter panel, a color picker, or a small settings menu that appears next to what you clicked. Unlike Dialog it doesn't block the rest of the page or trap focus behind a backdrop; it just opens near its trigger and closes on outside interaction or Escape.

## Install and import

Import behavior from `@askrjs/ui/popover` and the themed version from `@askrjs/themes/popover`. The root package re-exports the same set — `Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverClose` — so use whichever import path fits your bundling setup.

## Live examples

`Popover` wraps a `PopoverTrigger` and a `PopoverPortal` containing `PopoverContent`; put a `PopoverClose` inside the content if you want an explicit dismiss button rather than relying on outside-click. Pass `side` and `align` on `PopoverContent` to control which edge of the trigger it opens from and how it lines up against it.

## Anatomy

The tree is `Popover` &gt; `PopoverTrigger` and `PopoverPortal` &gt; `PopoverContent`, with `PopoverClose` optionally nested inside the content. There's no separate overlay component — popovers don't dim the background, since they're meant to be lightweight and non-blocking compared to Dialog.

## State model

Open state works exactly like Dialog's — `open`, `defaultOpen`, `onOpenChange` on the root — but there's no `modal` prop, because a popover never blocks the rest of the page by design. Positioning state (which side it actually rendered on, once it's flipped to avoid the viewport edge) is tracked internally and exposed through `data-side`/`data-align` attributes rather than anything you manage yourself.

## Keyboard and accessibility

`PopoverContent` renders with `role="dialog"` and is labelled from the trigger by default, with `aria-labelledby`/`aria-label` as your override if the trigger's own text isn't descriptive enough. It uses `FocusScope` for focus management, but not in trapped mode — Tab can move focus back out to the rest of the page while the popover is open, unlike Dialog's hard trap; what it does give you is restoring focus to the trigger on close. It sits behind a dismissable layer so outside clicks and Escape close it the way users expect from any transient overlay.

## Styling and tokens

`PopoverContent` accepts `side`, `align`, `sideOffset`, and a `width` of `sm`/`md`/`lg`, all of which show up as `data-side`/`data-align` attributes the default theme keys its positioning CSS off of. It sits at `--ak-z-popover` (`1500`) in the shared z-index scale — tied with `--ak-z-dropdown` at the same layer, above modals (`1400`) but below toast (`1550`) and tooltip (`1600`).

## API

`PopoverContent` layers `forceMount`, `side`, `align`, `sideOffset`, and `width` on top of standard div props. `PopoverTrigger` and `PopoverClose` are the usual button-like components with `onPress`/`disabled`. `PopoverPortal` just wraps `children` — it doesn't have positioning props of its own, since that all lives on `PopoverContent`.

## Edge cases

If the popover is anchored near a viewport edge, the positioning logic will flip `side` automatically to keep it visible — don't hardcode layout assumptions based on the `side` prop you passed in, read the resolved `data-side` attribute if you need to react to where it actually landed. Popovers containing form inputs need care around outside-click dismissal — a click that starts inside the content and ends outside (e.g. a native `<select>` dropdown) can register as an outside interaction if you're not careful with `onInteractOutside`.

## Related pages

Hover Card is a close cousin for content that should appear on hover rather than click. If you need to block the rest of the page and trap focus for something more significant than a small panel, use Dialog instead.

## Documentation navigation

[Previous](https://askrjs.com/docs/components/alert-dialog/index.md) | [Next](https://askrjs.com/docs/components/hover-card/index.md)
