Adaptive Popup
A responsive popup that renders a Dialog on desktop and a Drawer on mobile.
Preview
Basic Usage
Expanded Mode
Installation
Note: This component requires the useIsMobile hook from shadcn (installed with npx shadcn@latest add sidebar or available at hooks/use-mobile.tsx).
Usage
AdaptivePopup uses a slot-based API. Compose your popup using slot components as children:
Slot Components
- PopupHeader — Renders in the header region (DrawerHeader on mobile, DialogHeader on desktop)
- PopupSubHeader — A non-scrolling toolbar/filter row below the header
- PopupContent — The scrollable body content
- PopupFooter — A sticky footer region
- PopupRecyclingContent — Replaces PopupContent with a virtualized grid using RecyclingView
Children not wrapped in a slot component are treated as content when PopupContent is absent.
Layout Locking
The Dialog/Drawer choice is locked while the popup is open. This prevents remounting children when the viewport changes during an open popup, preserving internal state.
API Reference
AdaptivePopup
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Whether the popup is open |
onOpenChange | (open: boolean) => void | - | Called when the open state changes |
trigger | ReactNode | - | Optional trigger element. Omit when opening programmatically |
contentClassName | string | - | Fallback className for content when PopupContent slot is not used |
expanded | boolean | false | Expand dialog to near-full-screen on desktop |
children | ReactNode | - | Slot components and/or content |
PopupRecyclingContent
| Prop | Type | Default | Description |
|---|---|---|---|
groups | { name: string; items: T[] }[] | - | Grouped items to render in a virtualized grid |
renderItem | (item: T, groupIndex: number) => ReactNode | - | Render function for each item |
gridSize | GridSize | "small" | Grid size variant |
itemsPerPage | number | 60 | Items per virtualization page |