# PopoverPanel
**📖 Live documentation:** https://cds.coinbase.com/components/overlay/PopoverPanel/
PopoverPanel anchors an elevated floating panel to a trigger element.
## Import
```tsx
import { PopoverPanel } from '@coinbase/cds-web/popover'
```
## Examples
### Basics
Pass `content` for the panel body and `children` as the trigger. The trigger toggles open and closed on press; the panel applies focus management and escape-to-close behavior.
```jsx live
function BasicExample() {
return (
(
Panel titleArbitrary content for a floating panel.
)}
accessibilityLabel="Example settings panel"
>
);
}
```
### Selectable list
Use [`ListCell`](/components/data-display/ListCell/) with local state for the selected row and `closePopover` from the `content` render callback. You do not need `SelectProvider` or `SelectContext`.
After a value is chosen, the trigger often shows only the title. Set **`accessibilityLabel`** on the trigger to include the same details a sighted user gets from the list (for example title and description). Optionally set the panel **`accessibilityLabel`** so the dialog name matches the task (first choice vs. changing the value).
```jsx live
function ListCellSelectExample() {
const [selectedId, setSelectedId] = useState(null);
const options = [
{ id: 'eth', title: 'Ethereum', description: 'Main network' },
{ id: 'base', title: 'Base', description: 'L2 network' },
{ id: 'sol', title: 'Solana', description: 'External wallet' },
];
const selected = options.find((o) => o.id === selectedId);
return (
(
{options.map((option) => (
{
setSelectedId(option.id);
closePopover();
}}
/>
))}
)}
>
);
}
```
### Overlay and placement
Use `showOverlay` to dim content behind the panel. Adjust floating placement with `contentPosition` (see [Floating UI placement](https://floating-ui.com/docs/useFloating#placement)).
```jsx live
function OverlayAndPlacementExample() {
return (
(
Content with overlay and top placement.
)}
showOverlay
accessibilityLabel="Panel with overlay"
>
(
Content with overlay and top placement.
)}
contentPosition={{ placement: 'top', gap: 1 }}
accessibilityLabel="Panel above trigger"
>
);
}
```
### Panel sizing
By default, the panel content uses the same width as the trigger. Set `panelWidth`, `minPanelWidth`, `maxPanelWidth`, and `maxPanelHeight` when you need different constraints. The default max height is exported as `POPOVER_PANEL_MAX_HEIGHT`.
```jsx live
function SizingExample() {
return (
(
{Array.from({ length: 12 }, (_, i) => (
Row {i + 1}
))}
)}
panelWidth={280}
maxPanelHeight={200}
accessibilityLabel="Scrollable panel"
>
);
}
```
### Mobile modal
On small viewports, pass `enableMobileModal` to render the panel in a modal shell instead of a floating popover.
```jsx live
function MobileModalExample() {
return (
(
Modal-style panel
Useful when the floating surface would be cramped on phone breakpoints.
)}
enableMobileModal
accessibilityLabel="Settings in modal"
panelWidth={320}
maxPanelWidth="80vw"
>
);
}
```
### Imperative open and close
Use a ref to call `openPopover` and `closePopover` when you need to drive visibility from elsewhere (for example, a separate control or analytics callback).
```jsx live
function ImperativeExample() {
const panelRef = useRef(null);
return (
Panel opened from an external button.
}
accessibilityLabel="Programmatic panel"
>
);
}
```
## Props
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | `null \| string \| number \| false \| true \| ReactElement> \| Iterable \| ReactPortal` | Yes | `-` | Subject of the Popover that when interacted with will toggle the visibility of the content |
| `content` | `ReactNode \| PopoverPanelRenderContent` | Yes | `-` | Panel body, or a function that receives closePopover (helpfulwhen actions inside the panel should dismiss it). |
| `block` | `boolean` | No | `-` | Makes the Popover Subject fill the width of the parent container |
| `className` | `string` | No | `-` | - |
| `classNames` | `{ content?: string; triggerContainer?: string \| undefined; } \| undefined` | No | `-` | - |
| `contentPosition` | `PopoverContentPositionConfig` | No | `-` | Override content positioning defaults |
| `controlledElementAccessibilityProps` | `{ id: string; accessibilityLabel?: string; } \| undefined` | No | `-` | - |
| `disablePortal` | `boolean` | No | `-` | Does not render the panel inside of a portal (react-dom createPortal). Portal is automatically disabled for SSR |
| `disableTypeFocus` | `boolean` | No | `-` | Use for editable Search Input components to ensure focus is correctly applied |
| `disabled` | `boolean` | No | `-` | Prevents the panel from opening. Youll need to surface disabled state on the trigger manually. |
| `enableMobileModal` | `boolean` | No | `-` | Enable to have PopoverPanel render its content inside a Modal as opposed to a relatively positioned Popover. Ideal for mobile or smaller devices. |
| `key` | `Key \| null` | No | `-` | - |
| `maxPanelHeight` | `ResponsiveProp>` | No | `300` | Can optionally pass a maxHeight. |
| `maxPanelWidth` | `ResponsiveProp>` | No | `-` | Maximum width of the panel as a percentage string or number converted to pixels. |
| `minPanelWidth` | `ResponsiveProp>` | No | `-` | Minimum width of the panel as a percentage string or number converted to pixels. |
| `onBlur` | `(() => void)` | No | `-` | Callback that fires when PopoverPanel or trigger are blurred |
| `onClose` | `(() => void)` | No | `-` | Callback that fires when PopoverPanel is closed |
| `onOpen` | `(() => void)` | No | `-` | Callback that fires when PopoverPanel is opened |
| `panelHeight` | `ResponsiveProp>` | No | `-` | Height of the panel as a percentage string or number converted to pixels. |
| `panelWidth` | `ResponsiveProp>` | No | `-` | Width of the panel as a percentage string or number converted to pixels. |
| `ref` | `null \| string \| RefObject \| (instance: HTMLDivElement \| null) => void` | No | `-` | Allows getting a ref to the component instance. Once the component unmounts, React will set ref.current to null (or call the ref with null if you passed a callback ref). |
| `respectNegativeTabIndex` | `boolean` | No | `-` | If true, the focus trap will respect negative tabIndex values, removing them from the list of focusable elements. |
| `restoreFocusOnUnmount` | `boolean` | No | `true` | If true, the focus trap will restore focus to the previously focused element when it unmounts. WARNING: If you disable this, you need to ensure that focus is restored properly so it doesnt end up on the body |
| `showOverlay` | `boolean` | No | `-` | Display an overlay over all content below the Popover menu |
| `style` | `CSSProperties` | No | `-` | - |
| `styles` | `{ content?: CSSProperties; triggerContainer?: CSSProperties \| undefined; } \| undefined` | No | `-` | - |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
## Styles
| Selector | Static class name | Description |
| --- | --- | --- |
| `content` | `-` | elevated panel surface (PopoverPanelContent). |
| `triggerContainer` | `-` | wrapper around children (the Popover root in floating layout, or the trigger Box in the mobile modal). |