> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Popover - Web

> Popovers are floating containers that open on demand.

> [!NOTE]
>
> For development, import `Popover` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=159-692).

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/example-of-a-popover.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=25b03b3992dc525ebccf0e36bb67c7b5)

## Anatomy

The Popover consists of three primary elements that work together to create floating containers that open on demand.

![Anatomy of a
Popover](https://mintcdn.com/servicetitan/aip5_7K1pHSn1Axn/images/docs/web/components/popover/design/anatomy-of-a-popover.png?fit=max\&auto=format\&n=aip5_7K1pHSn1Axn\&q=85\&s=45ccf1cd0d44e7fca5c518f28b271c54)

1. Popover background, content
2. Caret
3. Trigger

## Options

The Popover supports the following configurations to accommodate various overlay scenarios.

### Content

Popovers can accept both text and custom content.

popover-content

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-content?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <div>
      <Flex justifyContent="center">
        <Grid
          placeItems="center"
          templateColumns="repeat(2, 1fr)"
          style={{
            paddingTop: "100px",
            gap: "150px",
          }}
        >
          <Popover open placement="top" disableShift disableFlip modal>
            <Popover.Button>Trigger</Popover.Button>
            <Popover.Content>Popover content </Popover.Content>
          </Popover>

          <Popover open placement="top" disableShift disableFlip modal>
            <Popover.Button>Trigger</Popover.Button>
            <Popover.Content>
              <Flex direction="column">
                <b>Headline content</b>
                Popover content
              </Flex>
            </Popover.Content>
          </Popover>
        </Grid>
      </Flex>
    </div>
  );
}

export default App;
```

### Placement

Note that placement of a Popover will adjust when not enough space is available.

popover-placement-top

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement-top?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  const placements = ["top-end", "top", "top-start"] as const;
  return (
    <Flex
      gap="2"
      direction="column"
      style={{
        padding: "0 16px",
      }}
    >
      {placements.map((placement) => {
        return (
          <Flex
            key={placement}
            alignItems="flex-end"
            justifyContent="center"
            style={{ minHeight: "120px", minWidth: "240px" }}
          >
            <Popover
              disableShift
              disableFlip
              modal
              open
              placement={placement}
              key={placement}
            >
              <Popover.Button style={{ gridArea: placement }}>
                Button
              </Popover.Button>
              <Popover.Content>{placement} placement</Popover.Content>
            </Popover>
          </Flex>
        );
      })}
    </Flex>
  );
}

export default App;
```

popover-placement-bottom

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement-bottom?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  const placements = ["bottom-end", "bottom", "bottom-start"] as const;
  return (
    <Flex
      gap="2"
      direction="column"
      style={{
        padding: "0 16px",
      }}
    >
      {placements.map((placement) => {
        return (
          <Flex
            key={placement}
            alignItems="flex-start"
            justifyContent="center"
            style={{ minHeight: "120px", minWidth: "240px" }}
          >
            <Popover
              disableShift
              disableFlip
              modal
              open
              placement={placement}
              key={placement}
            >
              <Popover.Button style={{ gridArea: placement }}>
                Button
              </Popover.Button>
              <Popover.Content>{placement} placement</Popover.Content>
            </Popover>
          </Flex>
        );
      })}
    </Flex>
  );
}

export default App;
```

popover-placement-left

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement-left?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  const placements = ["left-end", "left", "left-start"] as const;
  return (
    <Flex
      gap="2"
      direction="column"
      style={{
        padding: "0 16px",
      }}
    >
      {placements.map((placement) => {
        return (
          <Flex
            key={placement}
            alignItems="center"
            justifyContent="center"
            style={{ minHeight: "120px", minWidth: "240px" }}
          >
            <Popover
              disableShift
              disableFlip
              modal
              open
              placement={placement}
              key={placement}
            >
              <Popover.Button style={{ gridArea: placement }}>
                Button
              </Popover.Button>
              <Popover.Content>
                {placement} {"\n"} placement
              </Popover.Content>
            </Popover>
          </Flex>
        );
      })}
    </Flex>
  );
}

export default App;
```

popover-placement-right

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement-right?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  const placements = ["right-end", "right", "right-start"] as const;
  return (
    <Flex
      gap="2"
      direction="column"
      style={{
        padding: "0 16px",
      }}
    >
      {placements.map((placement) => {
        return (
          <Flex
            key={placement}
            alignItems="center"
            justifyContent="center"
            style={{ minHeight: "120px", minWidth: "240px" }}
          >
            <Popover
              disableShift
              disableFlip
              modal
              open
              placement={placement}
              key={placement}
            >
              <Popover.Button style={{ gridArea: placement }}>
                Button
              </Popover.Button>
              <Popover.Content>
                {placement} {"\n"} placement
              </Popover.Content>
            </Popover>
          </Flex>
        );
      })}
    </Flex>
  );
}

export default App;
```

## Behavior

The Popover responds to space constraints with automatic positioning adjustments and flexible opening/closing behaviors.

### Positioning Adjustments

The Popover makes automatic adjustments to its placement and dimensions when space is limited. It will:

* Auto flip when not enough space in the viewport is present.
* Resize the Popover when not enough space is present.
* Shift the Popover when scrolling to keep it in view.

These options can be turned off as needed.

### Opening a Popover

Popovers can be configured to open in multiple ways.

#### Open on click

popover-openonhover-false

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-openonhover-false?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid
      placeItems="center"
      style={{
        paddingTop: "70px",
      }}
    >
      <Popover placement="top">
        <Popover.Button>Click to open</Popover.Button>
        <Popover.Content>Popover content </Popover.Content>
      </Popover>
    </Grid>
  );
}

export default App;
```

#### Open on hover

popover-openonhover-true

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-openonhover-true?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid
      placeItems="center"
      style={{
        paddingTop: "70px",
      }}
    >
      <Popover placement="top" openOnHover>
        <Popover.Button>Hover to open</Popover.Button>
        <Popover.Content>Popover content </Popover.Content>
      </Popover>
    </Grid>
  );
}

export default App;
```

### Closing a Popover

Popovers can be configured to close in multiple ways.

#### Close on click outside

By default Popovers close on click outside. Override this behavior as needed.

popover-disablecloseonclickoutside-false

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-disablecloseonclickoutside-false?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid
      placeItems="center"
      style={{
        paddingTop: "70px",
      }}
    >
      <Popover placement="top">
        <Popover.Button>Close on click outside</Popover.Button>
        <Popover.Content>Popover content </Popover.Content>
      </Popover>
    </Grid>
  );
}

export default App;
```

#### Close action in the Popover

An action inside the Popover can close it. The Popover itself does not provide a set style for showing this.

popover-close-button

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-close-button?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Grid } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid
      placeItems="center"
      style={{
        paddingTop: "100px",
      }}
    >
      <Popover placement="top">
        <Popover.Button>Close with apply button</Popover.Button>
        <Popover.Content
          style={{ display: "flex", gap: 20, flexDirection: "column" }}
        >
          Popover content <Popover.Close>Apply</Popover.Close>
        </Popover.Content>
      </Popover>
    </Grid>
  );
}

export default App;
```

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoCover use cases other components don't | Popovers are small overlays that provide additional information or actions related to the element that triggers it. When a use case isn't covered by other components, Popovers can be used to create a UI pattern that the design system has not yet established, such as a tour flow. |
| DoPosition to minimize obstruction | Position Popovers to minimize obstruction of important elements, auto-flipping to stay in view and defaulting to a top position; see [Positioning a Popover](#positioning-a-popover). |
| Don'tUse Popover directly for common patterns | Popovers as a rule are not recommended to be used directly. The [Menu](https://anvil.servicetitan.com/docs/components/menu/web.md) and [Combobox](https://anvil.servicetitan.com/docs/components/combobox/web.md), both of which use Popover directly, cover most use cases for a Popover. Other overlay-type components, such as [Tooltips](https://anvil.servicetitan.com/docs/components/tooltip/web.md) and [Dialogs](https://anvil.servicetitan.com/docs/components/dialog/web.md), also cover use cases that a Popover could potentially use. |
| Don'tAdd Popovers to non-interactive elements | Popovers cannot be accessed by keyboard when behind a non-interactive element; see [Don't add Popovers to non-interactive elements](#dont-add-popovers-to-non-interactive-elements). |

### Alternatives

#### Popover vs Combobox

When a user needs to selects from many options, the Combobox should be used over the Popover. It is not recommended to recreate a combobox-like or select-like interaction with a Popover.

#### Popover vs Dialog

Both Dialogs and Popovers overlay elements on the page. In general, Dialogs are preferable when a situation requires user attention, and for more complicated flows.

| | Popover | Dialog |
| - | - | - |
| Content allowed in the container? | Any content | Any content |
| Complexity of content? | From simple to a few interactions | From simple to high complex |
| Backdrop applied to page? | No | Yes |

#### Popover vs Menu

When a list of actions need to take place, the Menu should be used. It is not recommended to recreate menu-like interactions through a Popover.

#### Popover vs Tooltip

Tooltip is the component of choice for tips and other informational overlays. Popovers may be used in place of a Tooltip when actions are needed.

| Usage | Tooltip | Popover |
| - | - | - |
| Can have a link? | Should not have interactive elements | Yes |
| Can have other interactive elements? | Should not have interactive elements | Yes |
| Open mechanic? | Hover or Focus | Click/Press, option for Hover or Focus |
| Can trap user focus? | No | Yes |

### How to Use

#### Positioning a Popover

* Popovers will auto flip and shift by default in order to stay in view. Define a starting position based on element placement: when elements are near a screen edge, position the Popover to open in the opposite direction.
* Position Popovers in a way that minimizes obstruction of important elements.
* If the above are not relevant, a top position is considered the default placement.

popover-placement-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement-do?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex, Grid, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <div>
      <Flex justifyContent="center" style={{ margin: "0 3rem" }}>
        <Grid
          columnGap="2"
          templateColumns="repeat(3, max-content)"
          placeContent="center"
          style={{
            paddingBottom: 100,
            rowGap: "150px",
          }}
        >
          <Popover placement="bottom-start" open modal>
            <Popover.Button data-interactive="active">Button</Popover.Button>
            <Popover.Content
              style={{ display: "flex", flexDirection: "column" }}
            >
              <b>Bottom start position</b>
              Popover content
            </Popover.Content>
          </Popover>
          <Button>Button</Button>
          <Button>Button</Button>

          <Button>Button</Button>
          <Button>Button</Button>
          <Popover placement="bottom-end" open modal>
            <Popover.Button data-interactive="active">Button</Popover.Button>
            <Popover.Content
              style={{ display: "flex", flexDirection: "column" }}
            >
              <b>Bottom end position</b>
              Popover content
            </Popover.Content>
          </Popover>
        </Grid>
      </Flex>
    </div>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

An example where the Popover position aligns with the position of a larger group of actions.

#### Don't add Popovers to non-interactive elements

Popovers cannot be accessed by keyboard when behind a non-interactive element. Consider adding this information on the page itself, or providing a dedicated help Button Icon.

popover-content-interactive-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-content-interactive-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Flex, Grid, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <div>
      <Flex justifyContent="center" style={{ margin: "0 3rem" }}>
        <Grid
          columnGap="2"
          templateColumns="repeat(3, max-content)"
          placeContent="center"
          style={{
            display: "grid",
            paddingTop: 120,
            rowGap: "150px",
          }}
        >
          <Button>Cancel</Button>
          <Popover placement="top-end" open modal>
            <Popover.Button disabled appearance="primary">
              Submit
            </Popover.Button>
            <Popover.Content
              style={{
                display: "flex",
                flexDirection: "column",
                maxWidth: "14rem",
              }}
            >
              Popover content that is explaining why the Submit button is
              disabled.
            </Popover.Content>
          </Popover>
        </Grid>
      </Flex>
    </div>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

## Content

Content within the Popover should clearly communicate the additional information or actions available.

## Keyboard Interaction

Users can navigate the Popover using standard keyboard controls.

| Key | Description |
| - | - |
| Space or Enter | When focusing on the trigger, opens the Popover |
| Escape | Closes the Popover when focus is on or inside the trigger or the content |
| Tab | When the Popover is `modal`, it cycles through all tab-able elements between the trigger and Popover, otherwise it behaves normally |

### Accessibility

#### Trapping Focus

Popovers by default do not trap focus. Content of the Popover maintains the DOM order, unlike portaling, which allows natural tab behavior to work accessibly without trapping the focus. Enable focus trapping by using the `modal` prop.

popover-focus

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-focus?file=src/App.tsx)

```tsx lines theme={null}
import { Popover, Grid, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Grid
      placeItems="center"
      gap="1"
      templateColumns="repeat(3, 1fr)"
      style={{
        paddingTop: 100,
      }}
    >
      <Button>Unrelated Button</Button>
      <Popover placement="top" modal>
        <Popover.Button appearance="primary">Open to trap focus</Popover.Button>
        <Popover.Content style={{ display: "flex", gap: 20 }}>
          <Button>Button 1</Button>
          <Button>Button 2</Button>
          <Button>Button 3</Button>
        </Popover.Content>
      </Popover>
      <Button>Unrelated Button</Button>
    </Grid>
  );
}

export default App;
```

For more guidance on managing focus with changing content, see [changing content best practices](https://anvil.servicetitan.com/docs/accessibility/changing-content.md).

## Code

**Implementation**

popover-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex justifyContent="center">
      <Popover placement="top" defaultOpen>
        <Popover.Button>Toggle Popover</Popover.Button>
        <Popover.Content>
          The <code>Popover.Content</code> accepts a <code>ReactNode</code>.
        </Popover.Content>
      </Popover>
    </Flex>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { Popover } from "@servicetitan/anvil2";

const ExampleComponent = () => {
  return (
    <Popover>
      <Popover.Button>Click me!</Popover.Button>
      <Popover.Content>This is the popover content.</Popover.Content>
    </Popover>
  );
};
```

### Popover basics

To build a popover, three components are required:

* `Popover`: the configurable parent component
* A way to control the open state of the popover. One of:
  * `Popover.Button`: an [Anvil2 Button](https://anvil.servicetitan.com/docs/components/button/react-native.md)
  * `Popover.Trigger`: used to render a custom trigger component
* `Popover.Content`: the content rendered within the popover

```tsx theme={null}
<Popover>
  <Popover.Button>Trigger Button</Popover.Button>
  <Popover.Content>Popover content. Accepts a ReactNode.</Popover.Content>
</Popover>
```

### Popover placement

Use the `placement` prop on the `Popover` to change the default positioning. The popover will reposition itself if there is not enough space in the default placement.

```tsx theme={null}
<Popover placement="top">...</Popover>
```

popover-placement

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-placement?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex, Grid } from "@servicetitan/anvil2";

function App() {
  const btnShared = { style: { width: "100%" }, size: "small" as const };

  return (
    <div>
      <Flex justifyContent="center" style={{ margin: "6rem 8rem" }}>
        <Grid templateColumns="repeat(3, 7rem)" gap="2">
          <Popover placement="top-start" defaultOpen>
            <Popover.Button {...btnShared}>top-start</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="top" defaultOpen>
            <Popover.Button {...btnShared}>top</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="top-end" defaultOpen>
            <Popover.Button {...btnShared}>top-end</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="left-start" defaultOpen>
            <Popover.Button {...btnShared}>left-start</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <div />
          <Popover placement="right-start" defaultOpen>
            <Popover.Button {...btnShared}>right-start</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="left" defaultOpen>
            <Popover.Button {...btnShared}>left</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <div />
          <Popover placement="right" defaultOpen>
            <Popover.Button {...btnShared}>right</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="left-end" defaultOpen>
            <Popover.Button {...btnShared}>left-end</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <div />
          <Popover placement="right-end" defaultOpen>
            <Popover.Button {...btnShared}>right-end</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="bottom-start" defaultOpen>
            <Popover.Button {...btnShared}>bottom-start</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="bottom" defaultOpen>
            <Popover.Button {...btnShared}>bottom</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
          <Popover placement="bottom-end" defaultOpen>
            <Popover.Button {...btnShared}>bottom-end</Popover.Button>
            <Popover.Content>Content</Popover.Content>
          </Popover>
        </Grid>
      </Flex>
    </div>
  );
}

export default App;
```

### Controlling popovers

Popovers can be controlled or uncontrolled (default). Control the popover using the `open` prop.

#### Uncontrolled (default)

popover-uncontrolled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-uncontrolled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <div>
      <Flex justifyContent="center" style={{ paddingTop: "4rem" }}>
        <Popover placement="top" defaultOpen>
          <Popover.Button>Toggle Popover</Popover.Button>
          <Popover.Content>Content</Popover.Content>
        </Popover>
      </Flex>
    </div>
  );
}

export default App;
```

#### Controlled

popover-controlled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-controlled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <div>
      <Flex justifyContent="center" style={{ paddingTop: "4rem" }}>
        <Popover placement="top" open={isOpen}>
          <Popover.Button onClick={() => setIsOpen((open) => !open)}>
            Toggle Popover
          </Popover.Button>
          <Popover.Content>Content</Popover.Content>
        </Popover>
      </Flex>
    </div>
  );
}

export default App;
```

### Using a custom trigger

To use something other than an [Anvil2 Button](https://anvil.servicetitan.com/docs/components/button/react-native.md) to trigger the popover, use the `Popover.Trigger` component. The `Popover.Trigger` passes a set of props to the `children` that should be passed to an interactive HTML element, such as a `button` or `input`.

```tsx theme={null}
<Popover>
  <Popover.Trigger>
    {(props) => <button {...props}>Open Popover</button>}
  </Popover.Trigger>
  <Popover.Content>Content</Popover.Content>
</Popover>
```

### Popover close button

The `Popover.Close` button can be used to close the popover from within the popover content.

popover-close

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-close?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex, Text } from "@servicetitan/anvil2";
import Close from "@servicetitan/anvil2/assets/icons/material/round/close.svg";

function App() {
  return (
    <div>
      <Flex justifyContent="center" style={{ paddingTop: "5rem" }}>
        <Popover placement="top" defaultOpen>
          <Popover.Button>Toggle Popover</Popover.Button>
          <Popover.Content>
            <Flex alignItems="center" gap="2">
              <Text>Press the close button:</Text>
              <Popover.Close icon={Close} />
            </Flex>
          </Popover.Content>
        </Popover>
      </Flex>
    </div>
  );
}

export default App;
```

On controlled popovers, the `onClick` prop should be used to update the open state:

```tsx theme={null}
<Popover.Close onClick={() => setIsOpenState(false)} icon={Close} />
```

### Disabling default closing behavior

By default, an [uncontrolled popover](#controlling-popovers) closes if either the user clicks outside of the popover, or the user presses the "Escape" key. Both of these behaviors can be disabled with props:

#### Disable close on outside click

popover-disablecloseonclickoutside

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-disablecloseonclickoutside?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex justifyContent="center">
      <Popover disableCloseOnClickOutside placement="top" defaultOpen>
        <Popover.Button>Toggle Popover</Popover.Button>
        <Popover.Content>
          This popover will not close if you click outside.
        </Popover.Content>
      </Popover>
    </Flex>
  );
}

export default App;
```

#### Disable close on "Escape" press

popover-disablecloseonescape

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-disablecloseonescape?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex justifyContent="center">
      <Popover disableCloseOnEscape placement="top" defaultOpen>
        <Popover.Button>Toggle Popover</Popover.Button>
        <Popover.Content>
          This popover will not close if you press the &quot;escape&quot; key.
        </Popover.Content>
      </Popover>
    </Flex>
  );
}

export default App;
```

### Enabling trapped focus

By default, the popover content does not trap focus so that users can navigate the content with a keyboard without naturally. To enable this behavior, set the `modal` prop of the `Popover` to `true`.

#### Default without trapping

popover-modal-false

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-modal-false?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex justifyContent="center">
      <Popover placement="top" defaultOpen>
        <Popover.Button>Toggle Popover</Popover.Button>
        <Popover.Content>
          <Flex gap="2" direction="column" style={{ maxWidth: "20rem" }}>
            <Text>
              Pressing &quot;tab&quot; will trap focus between this button and
              the popover trigger button.
            </Text>
            <Button>Example Button</Button>
          </Flex>
        </Popover.Content>
      </Popover>
    </Flex>
  );
}

export default App;
```

#### Trapped focus

popover-modal

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/popover-modal?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Popover, Flex, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap="4" justifyContent="center">
      <Popover placement="top" modal defaultOpen>
        <Popover.Button>Toggle Popover</Popover.Button>
        <Popover.Content>
          <Flex gap="2" direction="column" style={{ maxWidth: "20rem" }}>
            <Text>
              Pressing &quot;tab&quot; will focus on this button, then go to the
              button outside of this popover.
            </Text>
            <Button>Example Button</Button>
          </Flex>
        </Popover.Content>
      </Popover>
      <Button>Example Button</Button>
    </Flex>
  );
}

export default App;
```

### Using with Anvil1

`Popover` from version `1.23.0` uses HTML popover API which utilizes `top-layer`. This feature puts what's inside the `top-layer` to be on the top of entire the HTML. Because of this, there are cases where it does not work well with Anvil1's portaled components.

* While having the `Popover` open, if a portaled Anvil1 component is triggered to open from within, the `Popover` will be on top always. This is something we can potentially adjust in Anvil1 and we have a spike ticket internally for it. If you encounter this issue, please reach out to us.
* While having the `Popover` open, if a `Drawer` or `Dialog` from Anvil1 is triggered to open, not within the `Popover`, the `Popover` will be on top of the `Drawer` or `Dialog`.

## React Accessibility

Most of the accessibility considerations for managing `aria` props and keyboard focus are handled internally. There are some things to keep in mind while working with popovers:

* Always test keyboard navigation for interactive elements within popovers. The user should be able to easily access the elements in a logical order.
* If the popover includes a form, be sure to follow accessible best practices for form building.
* When building a custom trigger using `Popover.Trigger`, be sure to pass the props to an interactive element, as certain `aria` props are included to properly connect the trigger to the content.

For more guidance on creating accessible components, see [building accessible custom components best practices](https://anvil.servicetitan.com/docs/resources/contributing/accessible-custom-components.md).

**Popover Props**

```tsx theme={null}
<Popover
  defaultOpen={false}
  disableAutoUpdate={false}
  disableCaret={false}
  disableCloseOnEscape={false}
  disableCloseOnClickOutside={false}
  modal={true}
  noPadding={false}
  open={false}
  openOnHover={false}
  placement="bottom"
  onClose={() => {}}
  onClickOutside={() => {}}
  onOpenAnimationStart={() => {}}
  onOpenAnimationComplete={() => {}}
  onCloseAnimationStart={() => {}}
  onCloseAnimationComplete={() => {}}
>
  <Popover.Button>Trigger</Popover.Button>
  <Popover.Content>Content</Popover.Content>
</Popover>
```

## `Popover` Props

**children** `ReactNode`

Should include `Popover.Button` or `Popover.Trigger` and `Popover.Content`.

**defaultOpen** `boolean`

Controls the default open state on uncontrolled popovers.

**disableAutoUpdate** `boolean`

Disables all focus management entirely. Useful to delay focus management until
after a transition completes or some other conditional state.

**disableCaret** `boolean`

Removes the caret between the trigger and content.

**disableCloseOnEscape** `boolean`

See [Disabling default closing
behavior](https://anvil.servicetitan.com/docs/components/popover/code#disabling-default-closing-behavior)

**disableCloseOnClickOutside** `boolean`

See [Disabling default closing
behavior](https://anvil.servicetitan.com/docs/components/popover/code#disabling-default-closing-behavior)

**modal** `boolean` (default: true)

Traps focus within the popover content.

**noPadding** `boolean`

Removes padding from popover content.

**onClickOutside** `() =\> void`

Callback when clicking outside the popover.

**onClose** `() =\> void`

Called when the popover is closed, except after mouse exits when `openOnHover`
is `true`.

**onCloseAnimationComplete** `() =\> void`

Callback when closing animation completes.

**onCloseAnimationStart** `() =\> void`

Callback when closing animation starts.

**onOpenAnimationComplete** `() =\> void`

Callback when opening animation completes.

**onOpenAnimationStart** `() =\> void`

Callback when opening animation starts.

**open** `boolean`

Use to control open state of the popover.

**openOnHover** `boolean`

Opens the popover on hover over the trigger. In most cases, use a
[Tooltip](https://anvil.servicetitan.com/docs/components/tooltip/code) instead.

**placement** `"bottom" | "bottom-end" | "bottom-start" | "left" | "left-end" | "left-start" | "right" | "right-end" | "right-start" | "top" | "top-end" | "top-start"` (default: bottom)

**Popover.Button Props**

```tsx theme={null}
<Popover.Button appearance="primary" size="medium" onClick={() => {}}>
  Trigger Button
</Popover.Button>
```

## `Popover.Button` Props

The `Popover.Button` component accepts any valid [Anvil2 `Button` component props](https://anvil.servicetitan.com/docs/components/button/react-native#button-props), with one difference — the `children` prop is required in `Popover.Button`.

**Popover.Content Props**

```tsx theme={null}
<Popover.Content className="custom-class" id="popover-content">
  Popover content
</Popover.Content>
```

## `Popover.Content` Props

The `Popover.Content` component can accept any valid HTML `div` props.

**Popover.Trigger Props**

```tsx theme={null}
<Popover.Trigger>
  {(data) => (
    <button {...data} aria-expanded={data["aria-expanded"]} onClick={data.onClick}>
      Custom Trigger
    </button>
  )}
</Popover.Trigger>
```

## `Popover.Trigger` Props

**children** `(data) =\> ReactElement`

The `data` parameter includes `"aria-expanded"`, a `ref` for the trigger
component, and some additional accessibility fields.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.