Thumbprint logo

Components

Popover

Contextual dialogs anchored to an interactive element

Default

Pass the trigger as an element and the dialog contents as children. The trigger opens the popover without a separate click handler.

<PopoverV2
  accessibilityLabel="New feature"
  trigger={<ButtonV2>Open popover</ButtonV2>}
>
  <div>
    <div className="mb2">
      <Title
        headingLevel={2}
        size={5}
      >
        New feature
      </Title>
    </div>
    <Text size={2}>
      You can now estimate jobs from your settings.
    </Text>
    <div className="flex items-center justify-end gap1 mt3">
      <ButtonV2
        size="small"
        theme="tertiary"
      >
        Previous
      </ButtonV2>
      <ButtonV2 size="small">
        Next
      </ButtonV2>
    </div>
  </div>
</PopoverV2>

Controlled state

Control the popover when another part of the interface also needs to open or close it. Update state from onOpenChange so open and close requests stay synchronized.

function ControlledPopover() {
    const [isOpen, setIsOpen] = React.useState(false);

    return (
        <PopoverV2
            trigger={<ButtonV2>Open popover</ButtonV2>}
            isOpen={isOpen}
            onOpenChange={setIsOpen}
            accessibilityLabel="Estimate jobs"
        >
            <Title headingLevel={2} size={5}>Estimate jobs</Title>
            <Text size={2}>Create an estimate from your job settings.</Text>
        </PopoverV2>
    );
}

Positioning

Use position to choose the preferred side and alignment. React Aria automatically flips the popover when the preferred position would overflow the viewport.

The arrow tip sits 8px from the trigger by default. Use offset to override that gap in pixels when the context requires different spacing. The component accounts for the arrow size in every position, including when it flips.

<PopoverV2
  accessibilityLabel="Popover positioned below"
  position="bottom-start"
  trigger={<ButtonV2>Open popover</ButtonV2>}
>
  <Text size={2}>
    Popover content
  </Text>
</PopoverV2>

Supported values are top-start, top, top-end, bottom-start, bottom, bottom-end, left-start, left, left-end, right-start, right, and right-end.

Accessibility

Give the dialog a concise accessibilityLabel that identifies its purpose. Use a visible heading in the content, and keep the trigger label specific to the action it performs. The popover does not lock page scrolling or add a blocking underlay. The dialog retains keyboard focus containment. Closing with the close button or Escape returns focus to the trigger. Clicking non-focusable background content does not dismiss the popover; interacting with a focusable control outside can close it.

Trigger requirements

Use a React Aria-compatible interactive element as the trigger. Do not wrap a non-interactive element or recreate the v1 ref callback, because that bypasses the keyboard and accessibility behavior supplied by DialogTrigger.

Props

PopoverV2

  • children
    required

    Contents displayed inside the popover.

    Type
    React.ReactNode
  • trigger
    required

    Interactive element that opens the popover. Use a React Aria-compatible component such as ButtonV2 or LinkV2 so trigger semantics, focus, and keyboard behavior are preserved.

    Type
    React.ReactElement
  • position

    Preferred position of the popover relative to its trigger. The popover automatically flips when there is not enough room in the preferred direction.

    Type
    | 'top-start' | 'top' | 'top-end' | 'bottom-start' | 'bottom' | 'bottom-end' | 'left-start' | 'left' | 'left-end' | 'right-start' | 'right' | 'right-end'
    Default
    'top'
  • offset

    Gap in pixels between the arrow tip and the trigger. Defaults to the 8px spacing token.

    Type
    number
    Default
    tpV2SpaceXsmall
  • isOpen

    Controls whether the popover is open. Pair with onOpenChange for controlled usage.

    Type
    boolean
  • defaultOpen

    Sets the initial open state for uncontrolled usage.

    Type
    boolean
  • onOpenChange

    Called whenever the popover opens or closes.

    Type
    (isOpen: boolean) => void
  • onCloseClick

    Called when React Aria requests that the popover close, including the close button and Escape. Kept as a migration path from the V1 Popover API.

    Type
    () => void
  • accessibilityLabel

    Accessible name for the popover dialog.

    Type
    string
    Default
    'Popover'
  • dataTestId

    Selector hook for automated tests.

    Type
    string