Components

Popover

Reveal rich content in a floating panel anchored to a trigger.

Import

import { Popover, PopoverContent, PopoverTrigger } from "@dr-max/ui/popover"

The entry carries its own precompiled CSS — no extra style imports needed.

Usage

The popover is composed of three parts: Popover holds the open state, PopoverTrigger toggles it, and PopoverContent is the floating panel rendered in a portal. Pass as-child to the trigger to use your own element — typically a Button — instead of the default plain button. Click the trigger below to open it:

<script setup lang="ts">
import { Button } from "@dr-max/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@dr-max/ui/popover"
</script>

<template>
  <Popover>
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent>
      There are a lot of things you can do in space, and space essentially is
      unlimited resources.
    </PopoverContent>
  </Popover>
</template>

Side and align

The panel opens on side="top" centered on the trigger by default. Use side (top, right, bottom, left) and align (start, center, end) on PopoverContent to place it elsewhere — it flips automatically when there is not enough space:

<template>
  <Popover>
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent side="bottom" align="start">
      Opens below the trigger, aligned to its left edge.
    </PopoverContent>
  </Popover>
</template>

Offsets

side-offset sets the distance in px between the trigger and the panel (default 8); align-offset nudges the panel in px from its start/end alignment:

<template>
  <PopoverContent side="bottom" align="start" :side-offset="12" :align-offset="-4">
    …
  </PopoverContent>
</template>

Without arrow

Set :arrow="false" on PopoverContent to hide the arrow pointing at the trigger:

<template>
  <Popover>
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent :arrow="false">
      There are a lot of things you can do in space, and space essentially is
      unlimited resources.
    </PopoverContent>
  </Popover>
</template>

Open by default

Use default-open on Popover to render the popover open while keeping it uncontrolled:

<template>
  <Popover default-open>
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent>…</PopoverContent>
  </Popover>
</template>

Controlled state

Bind the open state with v-model:open when the consumer needs to own it (e.g. closing the popover from a button inside the panel):

<script setup lang="ts">
import { Button } from "@dr-max/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@dr-max/ui/popover"
import { ref } from "vue"

const open = ref(false)
</script>

<template>
  <Popover v-model:open="open">
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent>
      <Button variant="naked" @click="open = false">Close</Button>
    </PopoverContent>
  </Popover>
</template>

Set modal on Popover to disable interaction with outside elements while the popover is open:

<template>
  <Popover modal>
    …
  </Popover>
</template>

Playground

<script setup lang="ts">
import { Button } from '@dr-max/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@dr-max/ui/popover'
</script>

<template>
  <Popover>
    <PopoverTrigger as-child>
      <Button variant="secondary">Open popover</Button>
    </PopoverTrigger>
    <PopoverContent side="top" align="center" :side-offset="8" arrow>There are a lot of things you can do in space, and space essentially is unlimited resources.</PopoverContent>
  </Popover>
</template>
Dr. Max Design System • © 2026