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

PropTypeDefaultDescription
openboolean-Whether the popup is open
onOpenChange(open: boolean) => void-Called when the open state changes
triggerReactNode-Optional trigger element. Omit when opening programmatically
contentClassNamestring-Fallback className for content when PopupContent slot is not used
expandedbooleanfalseExpand dialog to near-full-screen on desktop
childrenReactNode-Slot components and/or content

PopupRecyclingContent

PropTypeDefaultDescription
groups{ name: string; items: T[] }[]-Grouped items to render in a virtualized grid
renderItem(item: T, groupIndex: number) => ReactNode-Render function for each item
gridSizeGridSize"small"Grid size variant
itemsPerPagenumber60Items per virtualization page