Components
Popover

Popover

An overlay that displays additional information or options when triggered.

Usage

import { XIcon } from 'lucide-react'
import { Box, Stack } from 'styled-system/jsx'
import { Button } from '~/components/ui/button'
import { IconButton } from '~/components/ui/icon-button'
import * as Popover from '~/components/ui/popover'

export const Demo = (props: Popover.RootProps) => {
  return (
    <Popover.Root {...props}>
      <Popover.Trigger asChild>
        <Button>Open Popover</Button>
      </Popover.Trigger>
      <Popover.Positioner>
        <Popover.Content>
          <Popover.Arrow>
            <Popover.ArrowTip />
          </Popover.Arrow>
          <Stack gap="1">
            <Popover.Title>Favorite Framework</Popover.Title>
            <Popover.Description>
              Tell us what is your favorite framework and why you love to use it.
            </Popover.Description>
          </Stack>
          <Box position="absolute" top="1" right="1">
            <Popover.CloseTrigger asChild>
              <IconButton aria-label="Close Popover" variant="ghost" size="sm">
                <XIcon />
              </IconButton>
            </Popover.CloseTrigger>
          </Box>
        </Popover.Content>
      </Popover.Positioner>
    </Popover.Root>
  )
}

Installation

1

Add Component

Insert code snippet into your project. Update import paths as needed.

import { Popover } from '@ark-ui/react/popover'
import type { ComponentProps } from 'react'
import { styled } from 'styled-system/jsx'
import { popover } from 'styled-system/recipes'
import { createStyleContext } from '~/lib/create-style-context'

const { withProvider, withContext } = createStyleContext(popover)

export const Root = withProvider(Popover.Root)
export const Anchor = withContext(styled(Popover.Anchor), 'anchor')
export const Arrow = withContext(styled(Popover.Arrow), 'arrow')
export const ArrowTip = withContext(styled(Popover.ArrowTip), 'arrowTip')
export const CloseTrigger = withContext(styled(Popover.CloseTrigger), 'closeTrigger')
export const Content = withContext(styled(Popover.Content), 'content')
export const Description = withContext(styled(Popover.Description), 'description')
export const Indicator = withContext(styled(Popover.Indicator), 'indicator')
export const Positioner = withContext(styled(Popover.Positioner), 'positioner')
export const Title = withContext(styled(Popover.Title), 'title')
export const Trigger = withContext(styled(Popover.Trigger), 'trigger')

export interface RootProps extends ComponentProps<typeof Root> {}
export interface AnchorProps extends ComponentProps<typeof Anchor> {}
export interface ArrowProps extends ComponentProps<typeof Arrow> {}
export interface ArrowTipProps extends ComponentProps<typeof ArrowTip> {}
export interface CloseTriggerProps extends ComponentProps<typeof CloseTrigger> {}
export interface ContentProps extends ComponentProps<typeof Content> {}
export interface DescriptionProps extends ComponentProps<typeof Description> {}
export interface IndicatorProps extends ComponentProps<typeof Indicator> {}
export interface PositionerProps extends ComponentProps<typeof Positioner> {}
export interface TitleProps extends ComponentProps<typeof Title> {}
export interface TriggerProps extends ComponentProps<typeof Trigger> {}
2

Add Recipe

This step is necessary only if you do not use any of the Park UI plugins.

import { popoverAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'

export const popover = defineSlotRecipe({
  className: 'popover',
  slots: popoverAnatomy.keys(),
  base: {
    positioner: {
      position: 'relative',
    },
    content: {
      background: 'bg.default',
      borderRadius: 'l3',
      boxShadow: 'lg',
      display: 'flex',
      flexDirection: 'column',
      maxWidth: 'sm',
      zIndex: 'popover',
      p: '4',
      _open: {
        animation: 'fadeIn 0.25s ease-out',
      },
      _closed: {
        animation: 'fadeOut 0.2s ease-out',
      },
      _hidden: {
        display: 'none',
      },
    },
    title: {
      fontWeight: 'medium',
      textStyle: 'sm',
    },
    description: {
      color: 'fg.muted',
      textStyle: 'sm',
    },
    closeTrigger: {
      color: 'fg.muted',
    },
    arrow: {
      '--arrow-size': 'var(--sizes-3)',
      '--arrow-background': 'var(--colors-bg-default)',
    },
    arrowTip: {
      borderTopWidth: '1px',
      borderLeftWidth: '1px',
    },
  },
})

On this page