{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "mobile-bottom-nav", "type": "registry:ui", "title": "MobileBottomNav", "description": "Data-driven bottom nav that collapses its items into a hamburger menu when they and the toolbar would overflow the viewport.", "categories": [ "navigation" ], "registryDependencies": [ "https://whiskeyjack.net/r/use-focus-trap.json", "https://whiskeyjack.net/r/use-nav-reset.json", "https://whiskeyjack.net/r/utils.json" ], "files": [ { "path": "components/ui/mobile-bottom-nav.tsx", "type": "registry:ui", "target": "components/ui/mobile-bottom-nav.tsx", "content": "import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type ElementType,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { useNavDoubleActivate } from \"@/hooks/use-nav-reset\";\n\nexport interface MobileNavItem {\n /** Stable key for React lists. */\n key: string;\n label: string;\n active?: boolean;\n /** Router target -- rendered via `linkComponent` (or a plain ``). */\n to?: string;\n /**\n * Click handler. Rendered as a `\n );\n };\n\n const renderBarItem = (item: MobileNavItem) =>\n renderNavEl(\n item,\n cn(BAR_ITEM, itemColor(!!item.active)),\n item.onClick,\n <>\n {item.renderIcon({ active: !!item.active, size: 24 })}\n {item.label}\n ,\n );\n\n return (\n
\n \n\n {/* Frosted menu above the bar; items as inline rows. */}\n {overflow && menuOpen && (\n <>\n
setMenuOpen(false)} aria-hidden />\n \n {navItems.map((item) =>\n renderNavEl(\n item,\n cn(MENU_ITEM, menuItemColor(!!item.active)),\n () => {\n setMenuOpen(false);\n item.onClick?.();\n },\n <>\n {item.renderIcon({ active: !!item.active, size: 20 })}\n {item.label}\n ,\n \"menuitem\",\n ),\n )}\n
\n \n )}\n
\n );\n});\n\nMobileBottomNav.displayName = \"MobileBottomNav\";\n" } ], "docs": "Give it navItems plus a toolbar. It measures the worst case (every toolbar action visible) so the collapse decision stays stable across pages. Router- and icon-agnostic: pass linkComponent for `to` items and a per-item renderIcon. Mobile-only (md:hidden), so pair it with a desktop HeaderNav.", "meta": { "group": "navigation", "related": [ "bottom-nav", "action-pill", "app-shell" ], "exports": [ "MobileBottomNav" ], "siteSlug": "mobile-bottom-nav" } }