---
title: "Popover"
description: "A non-modal dialog that floats around a trigger element."
canonical_url: "https://vinicunca.dev/pohon/components/popover"
---
# Popover

> A non-modal dialog that floats around a trigger element.

## Usage

Use a [Button](https://vinicunca.dev/pohon/components/button) or any other component in the default slot of the Popover.

Then, use the `#content` slot to add the content displayed when the Popover is open.

```vue
<template>
  <PPopover>
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

### Mode

Use the `mode` prop to change the mode of the Popover. Defaults to `click`.

> [!TIP]
> 
> In `hover` mode, set the `enable-touch` prop to let users toggle the Popover by tapping the trigger on touch devices, or use the `click` mode for triggers meant to be tapped.

```vue
<template>
  <PPopover mode="hover" enable-touch>
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

> [!NOTE]
> 
> When using the `hover` mode, the Akar [`HoverCard`](https://vinicunca.dev/akar/components/hover-card) component is used instead of the [`Popover`](https://vinicunca.dev/akar/components/popover).

### Delay

When using the `hover` mode, you can use the `open-delay` and `close-delay` props to control the delay before the Popover is opened or closed.

```vue
<template>
  <PPopover mode="hover" :open-delay="500" :close-delay="300">
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

### Content

Use the `content` prop to control how the Popover content is rendered, like its `align` or `side` for example.

```vue
<template>
  <PPopover :content="{
  align: 'center',
  side: 'bottom',
  sideOffset: 8
}">
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

### Arrow

Use the `arrow` prop to display an arrow on the Popover.

```vue
<template>
  <PPopover arrow>
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

### Modal

Use the `modal` prop to control whether the Popover blocks interaction with outside content. Defaults to `false`.

```vue
<template>
  <PPopover modal>
    <PButton label="Open" color="neutral" variant="subtle" />
  
    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template></PPopover>
</template>
```

### Dismissible

Use the `dismissible` prop to control whether the Popover is dismissible when clicking outside of it or pressing escape. Defaults to `true`.

> [!NOTE]
> 
> A `close:prevent` event will be emitted when the user tries to close it.

```vue [PopoverDismissibleExample.vue]
<template>
  <PPopover
    :dismissible="false"
    :ui="{ content: 'p-4' }"
  >
    <PButton
      label="Open"
      color="neutral"
      variant="subtle"
    />

    <template #content="{ close }">
      <div class="mb-4 flex gap-4 items-center">
        <h2 class="color-text-highlighted font-semibold">
          Popover non-dismissible
        </h2>

        <PButton
          color="neutral"
          variant="ghost"
          icon="i-lucide-x"
          @click="close"
        />
      </div>

      <Placeholder class="size-full min-h-48" />
    </template>
  </PPopover>
</template>
```

## Examples

### Control open state

You can control the open state by using the `default-open` prop or the `v-model:open` directive.

```vue [PopoverOpenExample.vue]
<script setup lang="ts">
const open = ref(false);

defineShortcuts({
  o: () => open.value = !open.value,
});
</script>

<template>
  <PPopover v-model:open="open">
    <PButton
      label="Open"
      color="neutral"
      variant="subtle"
    />

    <template #content>
      <Placeholder class="m-4 inline-flex size-48" />
    </template>
  </PPopover>
</template>
```

> [!NOTE]
> 
> In this example, leveraging [`defineShortcuts`](https://vinicunca.dev/pohon/composables/define-shortcuts), you can toggle the Popover by pressing `O`.

### With command palette

You can use a [CommandPalette](https://vinicunca.dev/pohon/components/command-palette) component inside the Popover's content.

```vue [PopoverCommandPaletteExample.vue]
<script setup lang="ts">
import type { CommandPaletteItem } from 'pohon-ui';

const items = ref([
  {
    label: 'bug',
    value: 'bug',
    chip: {
      color: 'error',
    },
  },
  {
    label: 'feature',
    value: 'feature',
    chip: {
      color: 'success',
    },
  },
  {
    label: 'enhancement',
    value: 'enhancement',
    chip: {
      color: 'info',
    },
  },
] satisfies Array<CommandPaletteItem>);

const label = ref([]);
</script>

<template>
  <PPopover :content="{ side: 'right', align: 'start' }">
    <PButton
      icon="i-lucide-tag"
      label="Select labels"
      color="neutral"
      variant="subtle"
    />

    <template #content>
      <PCommandPalette
        v-model="label"
        multiple
        placeholder="Search labels..."
        :groups="[{ id: 'labels', items }]"
        :ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
      />
    </template>
  </PPopover>
</template>
```

### With following cursor

You can make the Popover follow the cursor when hovering over an element using the [`reference`](https://vinicunca.dev/akar/components/tooltip#trigger) prop:

```vue [PopoverCursorExample.vue]
<script setup lang="ts">
const open = ref(false);
const anchor = ref({ x: 0, y: 0 });

const reference = computed(() => ({
  getBoundingClientRect: () =>
    ({
      width: 0,
      height: 0,
      left: anchor.value.x,
      right: anchor.value.x,
      top: anchor.value.y,
      bottom: anchor.value.y,
      ...anchor.value,
    } as DOMRect),
}));
</script>

<template>
  <PPopover
    :open="open"
    :reference="reference"
    :content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
  >
    <div
      class="text-sm border border-border-accented rounded-md border-dashed flex w-72 aspect-video items-center justify-center"
      @pointerenter="open = true"
      @pointerleave="open = false"
      @pointermove="(ev: PointerEvent) => {
        anchor.x = ev.clientX
        anchor.y = ev.clientY
      }"
    >
      Hover me
    </div>

    <template #content>
      <div class="p-4">
        {{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
      </div>
    </template>
  </PPopover>
</template>
```

### With anchor slot

You can use the `#anchor` slot to position the Popover against a custom element.

> [!WARNING]
> 
> This slot only works when `mode` is `click`.

```vue [PopoverAnchorSlotExample.vue]
<script lang="ts" setup>
const open = ref(false);
</script>

<template>
  <PPopover
    v-model:open="open"
    :dismissible="false"
    :ui="{ content: 'w-(--akar-popover-trigger-width) p-4' }"
  >
    <template #anchor>
      <PInput
        placeholder="Focus to open"
        @focus="open = true"
        @blur="open = false"
      />
    </template>

    <template #content>
      <Placeholder class="w-full aspect-square" />
    </template>
  </PPopover>
</template>
```

## API

### Props

```ts
/**
 * Props for the Popover component
 */
interface PopoverProps {
}
```

### Slots

```ts
/**
 * Slots for the Popover component
 */
interface PopoverSlots {
}
```

> [!NOTE]
> 
> The `close` function is only available when `mode` is set to `click` because Akar exposes this for [`Popover`](https://vinicunca.dev/akar/components/popover#close-using-slot-props) but not for [`HoverCard`](https://vinicunca.dev/akar/components/hover-card).

### Emits

No events available for this component.

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    popover: {
      slots: {
        content: 'rounded-md bg-background pointer-events-auto ring ring-border shadow-lg origin-$akar-popover-content-transform-origin focus:outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
        arrow: 'fill-background stroke-border'
      }
    }
  }
})
```

## Changelog

See commit history for [component](https://github.com/vinicunca/pohon/commits/main/src/runtime/components/Popover.vue) and [theme](https://github.com/vinicunca/pohon/commits/main/src/theme/popover.ts).

---

- [HoverCard](https://vinicunca.dev/akar/components/hover-card)
- [Popover](https://vinicunca.dev/akar/components/popover)
- [GitHub](https://github.com/vinicunca/pohon/blob/main/src/runtime/components/Popover.vue)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
