{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:react-aria-components';
import typesDocs from 'docs:@react-aria/overlays';
import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable} from '@react-spectrum/docs';
import styles from '@react-spectrum/docs/src/docs.css';
import packageData from 'react-aria-components/package.json';
import Anatomy from '/packages/react-aria/docs/overlays/popover-anatomy.svg';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
import {Divider} from '@react-spectrum/divider';
import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard';
import {ExampleList} from '@react-spectrum/docs/src/ExampleList';
import {Keyboard} from '@react-spectrum/text';
import {StarterKits} from '@react-spectrum/docs/src/StarterKits';

---
category: Overlays
keywords: [dialog, popover, aria]
type: component
---

# Popover

<PageDescription>{docs.exports.Popover.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['Popover']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/'}
  ]} />

## Example

```tsx example
import {DialogTrigger, Popover, Dialog, Button, OverlayArrow, Heading, Switch} from 'react-aria-components';

<DialogTrigger>
  <Button>Settings</Button>
  <Popover>
    <OverlayArrow>
      <svg width={12} height={12} viewBox="0 0 12 12"><path d="M0 0 L6 6 L12 0" /></svg>
    </OverlayArrow>
    <Dialog>
      <div className="flex-col">
        <Switch defaultSelected>
          <div className="indicator" /> Wi-Fi
        </Switch>
        <Switch defaultSelected>
          <div className="indicator" /> Bluetooth
        </Switch>
        <Switch>
          <div className="indicator" /> Mute
        </Switch>
      </div>
    </Dialog>
  </Popover>
</DialogTrigger>
```

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary>
```css hidden
@import './Button.mdx' layer(button);
@import './Dialog.mdx' layer(dialog);
@import './Switch.mdx' layer(switch);
```

```css
@import "@react-aria/example-theme";

.react-aria-Popover {
  --background-color: var(--overlay-background);

  border: 1px solid var(--border-color);
  box-shadow: 0 8px 20px rgba(0 0 0 / 0.1);
  border-radius: 6px;
  background: var(--background-color);
  color: var(--text-color);
  outline: none;
  max-width: 250px;
  transition: transform 200ms, opacity 200ms;
  --starting-scale: scale(0.9);
  transform-origin: var(--trigger-anchor-point);

  .react-aria-OverlayArrow svg {
    display: block;
    fill: var(--background-color);
    stroke: var(--border-color);
    stroke-width: 1px;
  }

  &[data-entering],
  &[data-exiting] {
    transform: var(--starting-scale) var(--origin);
    opacity: 0;
  }

  &[data-placement=top] {
    --origin: translateY(8px);

    &:has(.react-aria-OverlayArrow) {
      margin-bottom: 6px;
    }
  }

  &[data-placement=bottom] {
    --origin: translateY(-8px);

    &:has(.react-aria-OverlayArrow) {
      margin-top: 6px;
    }

    .react-aria-OverlayArrow svg {
      transform: rotate(180deg);
    }
  }

  &[data-placement=right] {
    --origin: translateX(-8px);

    &:has(.react-aria-OverlayArrow) {
      margin-left: 6px;
    }

    .react-aria-OverlayArrow svg {
      transform: rotate(90deg);
    }
  }

  &[data-placement=left] {
    --origin: translateX(8px);

    &:has(.react-aria-OverlayArrow) {
      margin-right: 6px;
    }

    .react-aria-OverlayArrow svg {
      transform: rotate(-90deg);
    }
  }
}
```

```css hidden
.flex-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
```

</details>

## Features

There is no built in way to create popovers in HTML. `Popover` helps achieve accessible popovers that can be styled as needed.

* **Styleable** – States for entry and exit animations are included for easy styling, and an optional arrow element can be rendered.
* **Accessible** – The trigger and popover are automatically associated semantically via ARIA. Content outside the popover is hidden from assistive technologies while it is open. The popover closes when interacting outside, or pressing the <Keyboard>Escape</Keyboard> key.
* **Focus management** – Focus is moved into the popover on mount, and restored to the trigger element on unmount.
* **Positioning** – The popover is positioned relative to the trigger element, and automatically flips and adjusts to avoid overlapping with the edge of the browser window.

Note: `Popover` only provides the overlay itself. It should be combined with [Dialog](Dialog.html) to create fully accessible popovers. Other overlays such as menus may also be placed in a popover.

## Anatomy

<Anatomy />

A popover consists of a trigger element (e.g. button) and an overlay, which is positioned relative to the trigger. The overlay may contain a [Dialog](Dialog.html), or another element such as a [Menu](Menu.html) or [ListBox](ListBox.html) when used within a component such as a [Select](Select.html) or [ComboBox](ComboBox.html).

```tsx
import {DialogTrigger, Popover, Dialog, Button, OverlayArrow} from 'react-aria-components';

<DialogTrigger>
  <Button />
  <Popover>
    <OverlayArrow />
    <Dialog />
  </Popover>
</DialogTrigger>
```

## Examples

<ExampleList tag="popover" />

## Starter kits

To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured [Storybook](https://storybook.js.org/) that you can experiment with, or use as a starting point for your own component library.

<StarterKits component="popover" />

## Reusable wrappers

If you will use a Popover in multiple places in your app, you can wrap all of the pieces into a reusable component. This way, the DOM structure, styling code, and other logic are defined in a single place and reused everywhere to ensure consistency.

This example wraps `Popover` and all of its children together into a single component. Since the `Dialog` is built in, this means it can't be used for components like Select, Menu, and ComboBox. Exclude the dialog if your popover will be reused in these components.

```tsx example export=true
import type {PopoverProps} from 'react-aria-components';
import {HelpCircle} from 'lucide-react';

interface MyPopoverProps extends Omit<PopoverProps, 'children'> {
  children: React.ReactNode
}

function MyPopover({children, ...props}: MyPopoverProps) {
  return (
    <Popover {...props}>
      <OverlayArrow>
        <svg width={12} height={12} viewBox="0 0 12 12"><path d="M0 0 L6 6 L12 0" /></svg>
      </OverlayArrow>
      <Dialog>
        {children}
      </Dialog>
    </Popover>
  );
}

<DialogTrigger>
  <Button aria-label="Help"><HelpCircle size={18} /></Button>
  <MyPopover>
    <Heading slot="title">Help</Heading>
    <p>For help accessing your account, please contact support.</p>
  </MyPopover>
</DialogTrigger>
```

## Positioning

### Placement

The popover's placement with respect to its anchor element can be adjusted using the `placement`
prop. See <TypeLink links={typesDocs.links} type={typesDocs.links[typesDocs.exports.Placement.id]} /> for a full list of available placement combinations.
Popovers will also automatically [flip](#flipping) to the opposite direction if there isn't enough space.

```tsx example
import {ArrowUp, ArrowDown, ArrowRight, ArrowLeft} from 'lucide-react';
<div style={{display: 'flex', gap: 8}}>
  <DialogTrigger>
    <Button><ArrowLeft size={18} /></Button>
    <MyPopover placement="start">In left-to-right, this is on the left. In right-to-left, this is on the right.</MyPopover>
  </DialogTrigger>
  <DialogTrigger>
    <Button><ArrowUp size={18} /></Button>
    <MyPopover placement="top">This popover is above the button.</MyPopover>
  </DialogTrigger>
  <DialogTrigger>
    <Button><ArrowDown size={18} /></Button>
    <MyPopover placement="bottom">This popover is below the button.</MyPopover>
  </DialogTrigger>
  <DialogTrigger>
    <Button><ArrowRight size={18} /></Button>
    <MyPopover placement="end">In left-to-right, this is on the right. In right-to-left, this is on the left.</MyPopover>
  </DialogTrigger>
</div>

```

### Offset and cross offset

The popover's offset with respect to its anchor element can be adjusted using the `offset` and
`crossOffset` props. The `offset` prop controls the spacing applied along the main axis between the element and its
anchor element whereas the `crossOffset` prop handles the spacing applied along the cross axis.

Below is a popover offset by an additional 50px above the trigger.

```tsx example
<DialogTrigger>
  <Button>Offset</Button>
  <MyPopover placement="top" offset={50}>
    Offset by an additional 50px.
  </MyPopover>
</DialogTrigger>
```

Below is a popover cross offset by an additional 100px to the right of the trigger.

```tsx example
<DialogTrigger>
  <Button>Cross offset</Button>
  <MyPopover placement="top" crossOffset={100}>
    Offset by an additional 100px.
  </MyPopover>
</DialogTrigger>
```

### Flipping

By default, `usePopover` attempts to flip popovers on the main axis in situations where the original placement
would cause it to render out of view. This can be overridden by setting `shouldFlip={false}`.
To see the difference between the two options, scroll this page so that the example below is near the bottom of the window.

```tsx example
<div className="flex-row">
  <DialogTrigger>
    <Button>Default</Button>
    <MyPopover placement="bottom">
      This is a popover that will flip if it can't fully render below the button.
    </MyPopover>
  </DialogTrigger>

  <DialogTrigger>
    <Button>shouldFlip=false</Button>
    <MyPopover placement="bottom" shouldFlip={false}>
      This is a popover that won't flip if it can't fully render below the button.
    </MyPopover>
  </DialogTrigger>
</div>
```

```css hidden
.flex-row {
  display: flex;
  gap: 8px;
}
```

### Container padding

You can control the minimum padding required between the popover and the
surrounding container via the `containerPadding` prop. This affects the positioning
breakpoints that determine when it will attempt to flip.

The example below will maintain at least 50px between the popover and the edge of the browser window.

```tsx example
<DialogTrigger>
  <Button>Container padding</Button>
  <MyPopover placement="top" containerPadding={50}>
    This is a popover.
  </MyPopover>
</DialogTrigger>
```

## Controlled open state

The above examples have shown `Popover` used within a `<DialogTrigger>`, which handles opening the popover when a button is clicked, and positioning relative to the trigger. This is convenient, but there are cases where you want to show a popover programmatically rather than as a result of a user action, or render the `<Popover>` in a different part of the JSX tree.

To do this, you can manage the popover's `isOpen` state yourself and provide it as a prop to the `<Popover>` element. The `onOpenChange` prop will be called when the user closes the popover, and should be used to update your state. In addition, the `triggerRef` prop must be set to the element that the popover should be positioned relative to.

```tsx example
function Example() {
  let [isOpen, setOpen] = React.useState(false);
  let triggerRef = React.useRef(null);

  return (
    <>
      <Button onPress={() => setOpen(true)}>Trigger</Button>
      <span ref={triggerRef} style={{paddingLeft: 12}}>Popover will be positioned relative to me</span>
      <MyPopover triggerRef={triggerRef} isOpen={isOpen} onOpenChange={setOpen}>
        <Heading slot="title">Popover</Heading>
        <div>I'm over here!</div>
      </MyPopover>
    </>
  );
}
```

## Custom trigger

`DialogTrigger` works out of the box with any pressable React Aria component (e.g. [Button](Button.html), [Link](Link.html), etc.). Custom trigger elements such as third party components and other DOM elements are also supported by wrapping them with the `<Pressable>` component, or using the [usePress](usePress.html) hook.

```tsx example
import {Pressable} from 'react-aria-components';

<DialogTrigger>
  {/*- begin highlight -*/}
  <Pressable>
    <span role="button">Custom trigger</span>
  </Pressable>
  {/*- end highlight -*/}
  <MyPopover>
    <Heading slot="title">Dialog</Heading>
    <p>This popover was triggered by a custom button.</p>
  </MyPopover>
</DialogTrigger>
```

Note that any `<Pressable>` child must have an [interactive ARIA role](https://www.w3.org/TR/wai-aria-1.2/#widget_roles) or use an appropriate semantic HTML element so that screen readers can announce the trigger. Trigger components must forward their `ref` and spread all props to a DOM element.

```tsx
const CustomTrigger = React.forwardRef((props, ref) => (
  <button {...props} ref={ref} />
));
```

## Props

### Popover

<PropTable component={docs.exports.Popover} links={docs.links} />

### OverlayArrow

`OverlayArrow` accepts all HTML attributes.

## Styling

React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin `className` attribute which can be targeted using CSS selectors. These follow the `react-aria-ComponentName` naming convention.

```css
.react-aria-Popover {
  /* ... */
}
```

A custom `className` can also be specified on any component. This overrides the default `className` provided by React Aria with your own.

```jsx
<Popover className="my-popover">
  {/* ... */}
</Popover>
```

In addition, some components support multiple UI states (e.g. focused, placeholder, readonly, etc.). React Aria components expose states using data attributes, which you can target in CSS selectors. For example:

```css
.react-aria-Popover[data-placement=left] {
  /* ... */
}
```

The `className` and `style` props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply, which is useful when using utility CSS libraries like [Tailwind](https://tailwindcss.com/).

```jsx
<OverlayArrow className={({placement}) => placement === 'left' || placement === 'right' ? 'rotate-90' : 'rotate-0'}>
  {/* ... */}
</OverlayArrow>
```

Popovers also support entry and exit animations via states exposed as data attributes and render props. `Popover` will automatically wait for any exit animations to complete before it is removed from the DOM. The `--trigger-anchor-point` variable is set to the position of the trigger relative to the popover, which is useful for origin-aware animations. See the [animation guide](styling.html#animation) for more details.

```css render=false
.react-aria-Popover {
  transition: opacity 300ms, scale 300ms;
  transform-origin: var(--trigger-anchor-point);

  &[data-entering],
  &[data-exiting] {
    opacity: 0;
    scale: 0.85;
  }
}
```

The states, selectors, and render props for each component used in a `Popover` are documented below.

### Popover

A `Popover` can be targeted with the `.react-aria-Popover` CSS selector, or by overriding with a custom `className`. It supports the following states and render props:

<StateTable properties={docs.exports.PopoverRenderProps.properties} />

Within a DialogTrigger, the popover will have the `data-trigger="DialogTrigger"` attribute. In addition, the `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the trigger button. The `--trigger-anchor-point` variable is set to the position of the trigger relative to the popover, which is useful for origin-aware animations.

```css render=false
.react-aria-Popover[data-trigger=DialogTrigger] {
  width: var(--trigger-width);
}
```

### OverlayArrow

A `OverlayArrow` can be targeted with the `.react-aria-OverlayArrow` CSS selector, or by overriding with a custom `className`. It supports the following states and render props:

<StateTable properties={docs.exports.OverlayArrowRenderProps.properties} />

## Advanced customization

### Contexts

All React Aria Components export a corresponding context that can be used to send props to them from a parent element. This enables you to build your own compositional APIs similar to those found in React Aria Components itself. You can send any prop or ref via context that you could pass to the corresponding component. The local props and ref on the component are merged with the ones passed via context, with the local props taking precedence (following the rules documented in [mergeProps](mergeProps.html)).

<ContextTable components={['Popover']} docs={docs} />

This example shows a `HelpTrigger` component that wraps a button and a popover and shows the popover when the user presses the <Keyboard>?</Keyboard> key. It uses `ButtonContext` to provide a keyboard listener and ref to a nested button, and `PopoverContext` to provide the trigger ref and open state to the nested popover.

```tsx example
import {ButtonContext, PopoverContext} from 'react-aria-components';

interface HelpTriggerProps {
  children?: React.ReactNode
}

function HelpTrigger({children}: HelpTriggerProps) {
  let triggerRef = React.useRef(null);
  let [isOpen, setOpen] = React.useState(false);
  let onKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === '?') {
      setOpen(true);
    }
  };

  return (
    <ButtonContext.Provider value={{onKeyDown, ref: triggerRef}}>
      {/*- begin highlight -*/}
      <PopoverContext.Provider value={{triggerRef, isOpen, onOpenChange: setOpen}}>
      {/*- end highlight -*/}
        {children}
      </PopoverContext.Provider>
    </ButtonContext.Provider>
  );
}

<HelpTrigger>
  <Button>Press ? for help</Button>
  <MyPopover>
    <Heading slot="title">Help</Heading>
    <div>Do you need help?</div>
  </MyPopover>
</HelpTrigger>
```

### Hooks

If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See [usePopover](usePopover.html) for more details.
