Popover
Displays rich content in a portal, triggered by a button. Built for React applications with Next.js integration and TypeScript support.
Ever notice how some of the best interfaces have these neat little panels that pop up right where you need them? Click a settings button and boom - there's your form right there. That's what popovers do best.
Settings panel in a popover
Clean way to show forms and controls without taking over the screen:
Built on Radix UI with all the smart positioning and keyboard stuff handled for you. It's one of those components that just works - figures out where to place itself, handles focus, plays nice with screen readers.
npx shadcn@latest add popover
When popovers make sense
You know that awkward spot between "this needs more than a tooltip" and "this doesn't need a whole dialog"? That's popover territory.
- Keep things contextual - Info shows up right where it belongs
- Save screen space - Hide secondary stuff until it's needed
- Smart about placement - Won't get cut off by viewport edges
- Handles focus properly - Keyboard navigation that actually works
- Renders in portals - No more z-index headaches
- Works with everything - Click, hover, focus, whatever you need
Useful popover patterns
Date picker
Because nobody wants to type dates manually:
Profile menu
Quick access to user stuff without a full dropdown:
These are the patterns you see everywhere because they work. Date pickers that don't suck, profile menus that feel snappy, settings panels that don't interrupt your flow.
Great for React apps
Works especially well in dashboards, admin panels, and anywhere you've got forms or controls that users need occasionally. The kind of stuff where a full page or modal would be overkill.
Drops right into Next.js projects. TypeScript definitions included. Plays nice with the shadcn ecosystem.
Built on solid foundations
Uses Radix UI under the hood, so you get all the good stuff without having to think about it:
- Collision detection - Bumps away from screen edges automatically
- Focus management - Tab navigation just works
- Portal rendering - No weird clipping issues
- Smooth animations - CSS variables for transitions that feel right
- Flexible anchoring - Stick it to whatever element makes sense
- Modal or not - Your choice on how it behaves
- Smart dismissal - Click outside, press escape, whatever feels natural
API Reference
Popover
The root container that manages popover state and provides context.
Prop | Type | Default | Description |
---|---|---|---|
defaultOpen | boolean | false | Default open state for uncontrolled usage |
open | boolean | - | Controlled open state |
onOpenChange | (open: boolean) => void | - | Callback when open state changes |
modal | boolean | false | Whether popover should be modal |
PopoverTrigger
The button that toggles the popover. By default, content positions against this element.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
asChild | boolean | false | Pass functionality to child element |
children | React.ReactNode | - | Trigger content |
Data attributes:
[data-state]
: "open" | "closed"
PopoverContent
The panel that appears when the popover is open. Renders in a portal by default.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
align | "start" | "center" | "end" | "center" | Alignment relative to trigger |
side | "top" | "right" | "bottom" | "left" | "bottom" | Preferred side of trigger |
sideOffset | number | 4 | Distance from trigger |
alignOffset | number | 0 | Offset from aligned position |
avoidCollisions | boolean | true | Whether to avoid viewport collisions |
collisionPadding | number | 10 | Padding from viewport edges |
arrowPadding | number | 0 | Padding between arrow and content edges |
sticky | "partial" | "always" | "partial" | Sticky behavior when scrolling |
hideWhenDetached | boolean | false | Hide when trigger becomes fully occluded |
Data attributes:
[data-state]
: "open" | "closed"[data-side]
: "left" | "right" | "bottom" | "top"[data-align]
: "start" | "end" | "center"
CSS variables:
--radix-popover-content-transform-origin
: Transform origin for animations--radix-popover-content-available-width
: Available width in viewport--radix-popover-content-available-height
: Available height in viewport--radix-popover-trigger-width
: Width of the trigger element--radix-popover-trigger-height
: Height of the trigger element
PopoverAnchor
Optional element to position content against instead of the trigger.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
asChild | boolean | false | Pass functionality to child element |
Event Handlers
The PopoverContent component supports several event handlers for fine-grained control:
Handler | Type | Description |
---|---|---|
onOpenAutoFocus | (event: Event) => void | Called when focus moves to content on open |
onCloseAutoFocus | (event: Event) => void | Called when focus returns to trigger on close |
onEscapeKeyDown | (event: KeyboardEvent) => void | Called when Escape key is pressed |
onPointerDownOutside | (event: PointerEvent) => void | Called when pointer is pressed outside |
onFocusOutside | (event: FocusEvent) => void | Called when focus moves outside |
onInteractOutside | (event: Event) => void | Called when interaction happens outside |
Keyboard Navigation
Key | Action |
---|---|
Space | Opens/closes the popover |
Enter | Opens/closes the popover |
Tab | Moves focus to next focusable element |
Shift + Tab | Moves focus to previous focusable element |
Escape | Closes popover and returns focus to trigger |
Positioning Patterns
Pattern | Use Case | Configuration |
---|---|---|
Bottom aligned | Dropdown menus | side="bottom" align="start" |
Top tooltip style | Help text | side="top" align="center" |
Side panels | Context menus | side="right" align="start" |
Corner dialogs | User profiles | side="bottom" align="end" |
Make them feel right
Few things to keep in mind so your popovers don't annoy people:
- Don't cram everything in - Just the stuff that matters for what they're doing
- Pick good spots - Usually bottom or right, but let collision detection do its thing
- Test with real content - Make sure long text doesn't break your layout
- Mobile matters - Fat fingers need bigger targets
- Keyboard users exist - Tab through it, make sure it makes sense
- Match the trigger - Click for settings, hover for previews, whatever feels obvious
- Close when done - Don't make people hunt for the X button
- Make it obvious - People should know what's clickable
Pagination
Navigation component for splitting large datasets across multiple pages. Built for React applications with Next.js integration and TypeScript support.
Progress
Displays an indicator showing the completion progress of a task. Built for React applications with Next.js integration and TypeScript support.