---
type: "package"
title: "@remix-run/ui/popover"
---

# @remix-run/ui/popover

`popover` provides headless behavior for anchored, dismissible floating panels. Use it for custom surfaces like filters, inspectors, and view options. More specific widgets like menu, select, and combobox build on it.

## Usage

```tsx
import { on, type Handle } from 'remix/component'
import * as popover from '@remix-run/ui/popover'
import { panelStyle } from './popover.styles'

export function ViewOptions(handle: Handle) {
  let open = false

  function openPopover() {
    open = true
    void handle.update()
  }

  function closePopover() {
    open = false
    void handle.update()
  }

  return () => (
    <popover.Context>
      <button
        mix={[
          popover.anchor({ placement: 'bottom-end' }),
          popover.focusOnHide(),
          on('click', openPopover),
        ]}
        type="button"
      >
        View options
      </button>

      <div
        mix={[
          panelStyle,
          popover.surface({
            open,
            onHide() {
              closePopover()
            },
          }),
        ]}
      >
        <button mix={[popover.focusOnShow(), on('click', closePopover)]} type="button">
          Close
        </button>
        <div>Panel content</div>
      </div>
    </popover.Context>
  )
}
```

## `@remix-run/ui/popover`

### `popover.Context`

Provides shared coordination for one popover instance. Render the anchor, any focus targets, and the surface inside the same context.

### `popover.anchor(options)`

Registers the host element as the anchor for the current popover surface.

### `popover.surface({ open, onHide, ... })`

Turns the host into the controlled popover surface.

- Wires `popover="manual"` and native `showPopover()` / `hidePopover()` behavior.
- Calls `onHide` for `Escape` and outside clicks with a `PopoverHideRequest`.
- Restores focus to the registered hide target unless `restoreFocusOnHide: false`.
- Accepts `closeOnAnchorClick: false` when the anchor must stay interactive while open.

Apply this to the actual floating root, not a nested child.

### `popover.focusOnShow()`

Registers the element that should receive focus when the popover opens.

### `popover.focusOnHide()`

Registers the element that should receive focus again when the popover closes.

### Primitive Types

- `PopoverContext`: context object exposed by `popover.Context`.
- `PopoverProps`: provider props for one popover instance.
- `PopoverSurfaceOptions`: options accepted by `popover.surface(...)`.
- `PopoverHideRequest`: hide request passed to `onHide`.

## Behavior Notes

- Opening anchors the surface to the registered anchor and locks page scrolling until close.
- `onHide` receives `{ reason: 'escape-key' | 'outside-click', target? }`.
- `popover.focusOnShow()` wins on open when present.
- `popover.focusOnHide()` is used on close by default when focus restoration is enabled.
- `closeOnAnchorClick: false` keeps anchor clicks inside the current session, which is useful for input-driven popovers like comboboxes.

