Popover

Free · MITReact Native · Expo

A bubble anchored to an element whose arrow always points at the anchor's center, even when the bubble is pushed in from the screen edge; it flips above or below to fit and unfurls out of the arrow tip.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/popover.json

Then run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/popover.

  • Wrap the app in ThemeProvider (the bubble renders in its overlay layer).

Usage

import { Popover } from "@/components/goodui-native/popover/popover";

<Popover
  variant="tooltip"
  placement="top"
  accessibilityLabel="About billing"
  content="Billed once a year. Cancel anytime."
>
  <InfoIcon color={colors.textSecondary} />
</Popover>

When to use

Use it for
  • A short explanation next to what it explains: an info icon, a chart value, a new feature.
  • A small piece of UI tied to a control, such as a guest count picker under a field.
Reach for something else
  • A list of actions on an element: use context-menu (or action-sheet for screen-level choices).
  • Content longer than a few lines or with several inputs: use bottom-sheet.
  • Confirmations: use alert-dialog.

Examples

Content with its own controls

<Popover
  title="Guests"
  align="start"
  accessibilityLabel={`Guests, ${guests}`}
  content={(close) => (
    <View style={{ gap: 12, width: 240 }}>
      <GuestStepper value={guests} onChange={setGuests} />
      <Button size="sm" onPress={close}>Done</Button>
    </View>
  )}
>
  <GuestField count={guests} />
</Popover>

Controlled coach mark

<Popover open={showTip} onOpenChange={setShowTip} title="Split the bill" content="Split any payment with the people in this chat.">
  <SplitChip />
</Popover>

API reference

PropTypeDefault
children*
The anchor. Popover makes it the button that opens the bubble, so pass plain content (icon, text, chip), not another button.
ReactNode—
content*
Bubble content. A string renders as text; a function receives `close`.
ReactNode | ((close: () => void) => ReactNode)—
title
Heading inside a popover bubble.
string—
open
Whether the bubble is shown (controlled).
boolean—
defaultOpen
Open on first render when uncontrolled.
booleanfalse
onOpenChange
Called when the bubble opens or closes.
(open: boolean) => void—
variant
`popover` is a raised card for content and actions; `tooltip` is a small inverted label that also opens while the anchor is held (native) or hovered (web).
"popover" | "tooltip""popover"
placement
Preferred side. It flips to the other side when the bubble does not fit.
"top" | "bottom""bottom"
align
Horizontal alignment with the anchor before it is clamped to the screen.
"center" | "start" | "end""center"
maxWidth
Maximum bubble width in points. Default 300 for popovers, 240 for tooltips.
number—
haptic
Haptic when a held tooltip appears. Default `true`.
booleantrue
accessibilityLabel
Label for the anchor button. Defaults to the anchor's text.
string—
anchorStyle
Style of the anchor wrapper.
StyleProp<ViewStyle>—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<PopoverLabels>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Open: the bubble grows out of the arrow tip; its height leads on the bouncy spring and its width follows on the morph spring, so it unfurls instead of just zooming, while it fades in.
  • Close: fades and shrinks to 0.9 toward the arrow.
  • Placement: prefers `placement`, flips to the other side when it does not fit, is clamped 12 pt from the screen edges, and the arrow slides along the bubble to stay on the anchor's center.
  • Reduced motion: no growth; the bubble fades in and out.
  • Haptic: select when a tooltip opens from a long press
  • Haptic: none when tapping or hovering
  • The bubble mounts only while open, measures its content once and animates on the UI thread.

Accessibility

  • The anchor is a button with accessibilityState expanded and a hint; pass accessibilityLabel when it has no text (an icon).
  • A popover bubble is modal for screen readers and announces its title when it opens; tooltips are a polite live region.
  • Tap outside, Android back or Escape on the web closes a popover; the anchor gets an 8 pt hitSlop so small icons reach 44 pt.
  • An accessibilityLabel on an anchor that shows text must start with that text (web browsers check it); leave it out to let the visible text name the anchor.

Notes for AI

  • children is the anchor and becomes the button: pass plain content (an icon, a chip, text), never another Button or Pressable.
  • Give rich content a fixed width (for example width: 240) so the bubble measures it once.
  • Tooltips (variant='tooltip') take a short string; they open on tap, while held (closing on release) and on hover on the web.
  • content can be a function receiving close, for a Done button inside the bubble.
  • The bubble is placed when it opens; it does not follow an anchor that scrolls while it is open (popovers block scrolling with their backdrop; hover tooltips close on hover out).