Mobile Bottom Navbar
Overview
This skill provides a complete, production-ready mobile bottom navigation system for Next.js 15+ applications with App Router. The implementation features:
- 3-Column Icon Grid Layouts for submenus and More menu
- Accordion-Based More Menu with collapsible group sections
- Submenu Dropdown appearing above the navbar
- Role-based filtering and smooth animations
📸 Visual Reference: See references/screenshots/ for actual UI examples from MyJKKN application.
Key Features
- Icon Grid Submenus: 3-column grid layout with icons + labels (like iOS app grid)
- Accordion More Menu: Collapsible sections showing all overflow menu groups
- Always Visible Design: Full navbar always shown on mobile (no separate minimized state)
- Role-Based Filtering: Dynamic navigation based on user permissions
- Smart Active Detection: Automatically highlights current page
- Submenu Expansion: Click nav items to expand submenus with backdrop overlay
- Overflow Handling: "More" menu for navigation groups beyond primary 4 items
- State Persistence: Zustand with localStorage for cross-session state
- Hydration Safety: Prevents flash of incorrect state on page load
- Smooth Animations: Spring-based animations via Framer Motion
- iOS Safe Area Support: Respects notch and home indicator spacing
- TypeScript First: Fully typed for excellent DX
- Customizable: Colors, animations, layouts, and icons
UI Pattern (Critical Design Elements)
✅ Bottom Navbar
┌─────────────────────────────────────────────────────┐
│ [Icon] [Icon] [Icon] [Icon] [Icon] │
│ Overview User Mgmt Apps App Hub More (5+) │
│ ─ │ ← Active indicator
└─────────────────────────────────────────────────────┘
- 4 primary items + "More" button with badge count
- Even spacing, icons with labels below
- Active state: primary color + underline indicator
┌─────────────────────────────────────┐
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 📊 │ │ 🤖 │ │ │ │ ← 3-column grid
│ │Dash │ │ AI │ │ │ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘
└─────────────────────────────────────┘ ← Navbar (below)
- Grid layout (3 columns)
- Icon + label cards
- Appears above navbar with backdrop
┌─────────────────────────────────────┐
│ All Menus ✕ │
├─────────────────────────────────────┤
│ > Organization Management 9 ▼ │ ← Accordion header with count
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 🏢 │ │ 🎓 │ │ 🔥 │ │ ← 3-column icon grid
│ │Inst │ │Degr │ │Dept │ │
│ └─────┘ └─────┘ └─────┘ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 📚 │ │ 📅 │ │ 📖 │ │
│ │Prog │ │Seme │ │Sect │ │
│ └─────┘ └─────┘ └─────┘ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 📝 │ │ 🗺️ │ │ │ │
│ │Cour │ │Maps │ │ │ │
│ └─────┘ └─────┘ └─────┘ │
│ │
│ > Finance Management 6 ▶ │ ← Collapsed section
│ │
└─────────────────────────────────────┘
Critical Pattern Elements:
- Sheet Component: Bottom sheet with rounded top (
rounded-t-3xl)
- Accordion:
type="multiple" with all groups expanded by default
- Group Headers: Icon badge + label + count + chevron
- Submenu Grid: 3-column layout with icons and labels
- Active Highlighting: Primary color ring and background
When to Use This Skill
Use this skill when:
- Implementing mobile navigation for Next.js 15+ App Router applications
- Needing role-based or permission-based navigation filtering
- Building internal applications with consistent navigation patterns
- Requiring persistent navigation state across sessions
- Supporting iOS devices with safe area insets
- Wanting smooth, professional animations without performance issues
- Need accordion-style More menu with icon grid submenus
Auto-Trigger Keywords
This skill automatically activates when you mention:
- "mobile nav", "mobile navigation", "mobile bottom navigation"
- "bottom navbar", "bottom navigation bar", "bottom bar"
- "add mobile nav", "create bottom bar", "implement mobile menu"
Quick Start
1. Install Dependencies
bash
1npm install zustand framer-motion lucide-react clsx tailwind-merge
Required shadcn/ui components:
bash
1npx shadcn@latest add sheet accordion scroll-area
2. Copy Core Files
Copy all files from assets/ to your project:
assets/
├── components/BottomNav/
│ ├── bottom-navbar.tsx → components/BottomNav/
│ ├── bottom-nav-item.tsx → components/BottomNav/
│ ├── bottom-nav-submenu.tsx → components/BottomNav/
│ ├── bottom-nav-more-menu.tsx → components/BottomNav/ ✨ KEY COMPONENT
│ ├── bottom-nav-minimized.tsx → components/BottomNav/
│ ├── types.ts → components/BottomNav/
│ └── index.ts → components/BottomNav/
└── hooks/
├── use-bottom-nav.ts → hooks/
└── use-mobile.tsx → hooks/
⚠️ IMPORTANT: Always use the complete bottom-nav-more-menu.tsx from assets to ensure proper accordion + icon grid pattern.
3. Integrate with Layout
typescript
1// app/(routes)/layout.tsx
2'use client';
3
4import { cn } from '@/lib/utils';
5import { useIsMobile } from '@/hooks/use-mobile';
6import { BottomNavbar } from '@/components/BottomNav';
7
8export default function MainLayout({ children }) {
9 const isMobile = useIsMobile();
10
11 return (
12 <>
13 <main className={cn(
14 'min-h-screen bg-background',
15 isMobile && 'pb-20' // Bottom padding for navbar
16 )}>
17 {children}
18 </main>
19
20 <BottomNavbar />
21 </>
22 );
23}
Create or update lib/sidebarMenuLink.ts:
typescript
1import { LucideIcon, Home, Users, Building, /* ... */ } from 'lucide-react';
2
3interface MenuItem {
4 href: string;
5 label: string;
6 icon: LucideIcon;
7 active: boolean;
8 submenus: Array<{ href: string; label: string; active: boolean }>;
9}
10
11interface MenuGroup {
12 groupLabel: string;
13 menus: MenuItem[];
14}
15
16// Example navigation structure
17export function GetRoleBasedPages(
18 pathname: string,
19 userRole?: CustomRole | null
20): MenuGroup[] {
21 return [
22 {
23 groupLabel: 'Overview',
24 menus: [
25 {
26 href: '/dashboard',
27 label: 'Dashboard',
28 icon: Home,
29 active: pathname === '/dashboard',
30 submenus: [
31 { href: '/dashboard', label: 'Dashboard', active: pathname === '/dashboard' },
32 { href: '/ai-assistant', label: 'AI Assistant', active: pathname === '/ai-assistant' }
33 ]
34 }
35 ]
36 },
37 {
38 groupLabel: 'User Management',
39 menus: [
40 {
41 href: '/users',
42 label: 'Users',
43 icon: Users,
44 active: pathname.startsWith('/users'),
45 submenus: [
46 { href: '/users/list', label: 'User List', active: pathname === '/users/list' },
47 { href: '/users/roles', label: 'Roles', active: pathname === '/users/roles' }
48 ]
49 }
50 ]
51 },
52 // ... first 4 groups appear in navbar
53 // Groups 5+ appear in More menu with accordion + grid layout
54 {
55 groupLabel: 'Organization Management',
56 menus: [
57 {
58 href: '/organization',
59 label: 'Organization',
60 icon: Building,
61 active: pathname.startsWith('/organization'),
62 submenus: [
63 { href: '/organization/institutions', label: 'Institutions', active: false },
64 { href: '/organization/degrees', label: 'Degrees', active: false },
65 { href: '/organization/departments', label: 'Departments', active: false },
66 { href: '/organization/programs', label: 'Programs', active: false },
67 { href: '/organization/semesters', label: 'Semesters', active: false },
68 { href: '/organization/sections', label: 'Sections', active: false },
69 { href: '/organization/courses', label: 'All Courses', active: false },
70 { href: '/organization/mappings', label: 'Course Mappings', active: false }
71 ]
72 }
73 ]
74 }
75 ];
76}
5. Verify Implementation
Test checklist:
- ✅ Navbar appears on mobile (< 1024px width)
- ✅ Hidden on desktop (≥ 1024px width)
- ✅ Active nav item highlighted
- ✅ Submenu expands ABOVE navbar in 3-column grid
- ✅ More menu opens as bottom sheet
- ✅ More menu shows accordion sections with icon grid
- ✅ All accordion groups expanded by default
- ✅ Group headers show icon + label + count
- ✅ Navigation works correctly
- ✅ State persists across refreshes
- ✅ iOS safe area insets respected
Critical Implementation Notes
The More menu MUST use this exact pattern:
typescript
1// bottom-nav-more-menu.tsx
2<Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>
3 <SheetContent side="bottom" className="h-[80vh] rounded-t-3xl">
4 <SheetHeader>
5 <SheetTitle>All Menus</SheetTitle>
6 </SheetHeader>
7
8 {/* Accordion with ALL groups expanded by default */}
9 <Accordion type="multiple" defaultValue={groups.map(g => g.id)}>
10 {groups.map((group) => (
11 <AccordionItem key={group.id} value={group.id}>
12 <AccordionTrigger>
13 <div className="flex items-center gap-3">
14 {/* Icon badge */}
15 <div className="p-2 rounded-lg bg-muted">
16 <GroupIcon className="h-4 w-4" />
17 </div>
18 {/* Label */}
19 <span className="font-medium text-sm">{group.groupLabel}</span>
20 {/* Count */}
21 <span className="text-xs text-muted-foreground ml-auto mr-2">
22 {group.menus.length}
23 </span>
24 </div>
25 </AccordionTrigger>
26
27 <AccordionContent>
28 {/* 3-column icon grid */}
29 <div className="grid grid-cols-3 gap-2 pt-2 pb-3">
30 {group.menus.map((item) => (
31 <button
32 key={item.href}
33 onClick={() => handleItemClick(item.href)}
34 className="flex flex-col items-center p-3 rounded-lg"
35 >
36 <Icon className="h-5 w-5 mb-1" />
37 <span className="text-[10px] text-center line-clamp-2">
38 {item.label}
39 </span>
40 </button>
41 ))}
42 </div>
43 </AccordionContent>
44 </AccordionItem>
45 ))}
46 </Accordion>
47 </SheetContent>
48</Sheet>
Key Requirements:
Accordion type="multiple" - Allows multiple sections open simultaneously
defaultValue={groups.map(g => g.id)} - All groups expanded by default
- 3-column grid (
grid-cols-3) for submenu items
- Icon + label for each item
- Group header shows count of items
Common Mistakes to Avoid
❌ DON'T create flat list More menu:
typescript
1// WRONG - Flat list layout
2<div>
3 {groups.map(group => (
4 <div key={group.id}>
5 <h3>{group.groupLabel}</h3>
6 <ul>
7 {group.menus.map(item => (
8 <li>{item.label}</li>
9 ))}
10 </ul>
11 </div>
12 ))}
13</div>
✅ DO use accordion with icon grid (see above)
❌ DON'T place submenu below navbar:
typescript
1// WRONG - Submenu below navbar
2<nav>...</nav>
3<Submenu /> // Wrong position
✅ DO place submenu inside navbar, above items:
typescript
1<nav>
2 <Submenu /> // Correct - inside nav, above items
3 <div className="flex">...</div>
4</nav>
File Organization
After implementation:
your-project/
├── components/
│ └── BottomNav/
│ ├── bottom-navbar.tsx (Main orchestrator)
│ ├── bottom-nav-item.tsx (Individual nav button)
│ ├── bottom-nav-submenu.tsx (3-column grid dropdown)
│ ├── bottom-nav-more-menu.tsx (Accordion + grid modal) ✨
│ ├── bottom-nav-minimized.tsx (Optional minimized state)
│ ├── types.ts (TypeScript definitions)
│ └── index.ts (Barrel export)
├── hooks/
│ ├── use-bottom-nav.ts (Zustand store)
│ └── use-mobile.tsx (Mobile detection)
├── lib/
│ ├── sidebarMenuLink.ts (Navigation structure)
│ └── utils.ts (cn utility)
└── app/
└── (routes)/
└── layout.tsx (Integration point)
Resources
References (references/)
-
screenshots/ - Visual reference from MyJKKN app
myjkkn-dashboard.png - Main navbar view
myjkkn-submenu-dropdown.png - 3-column submenu grid
myjkkn-more-menu.png - Accordion More menu with icon grid
README.md - Detailed visual documentation
-
complete-implementation.md - Full step-by-step implementation guide
-
component-reference.md - Complete API documentation
-
customization-guide.md - Theming and styling guide
-
integration-guide.md - Auth and layout integration
-
troubleshooting.md - Common issues and solutions
Assets (assets/)
components/BottomNav/ - Complete component implementations
hooks/ - State management and utilities
examples/ - Implementation examples
Usage Patterns
For New Projects
- Copy all files from
assets/ to appropriate locations
- Install dependencies
- Configure navigation structure
- Integrate with main layout
- Test on mobile devices
- Verify accordion More menu pattern
For Existing Projects
- Review
references/screenshots/ for visual pattern
- Copy latest components from
assets/
- Update More menu to use accordion + grid pattern
- Test for feature parity with screenshots
- Gradual rollout with feature flags
Troubleshooting Wrong UI Pattern
Problem: More menu shows flat list instead of accordion grid
Solution:
- Verify you copied latest
bottom-nav-more-menu.tsx from assets/
- Check accordion configuration:
typescript
1<Accordion type="multiple" defaultValue={groups.map(g => g.id)}>
- Check grid layout in AccordionContent:
typescript
1<div className="grid grid-cols-3 gap-2">
- Verify shadcn/ui accordion component is installed
Problem: Submenu appears below navbar
Solution:
- Check submenu placement in
bottom-navbar.tsx
- Ensure submenu is INSIDE
<nav> element, BEFORE nav items
- Verify z-index layering
Best Practices
- Always Use Latest Components: Copy from
assets/ folder, not custom implementations
- Reference Screenshots: Check
references/screenshots/ for correct UI pattern
- Test Icon Grid: Verify 3-column grid layout in submenus and More menu
- Verify Accordion: Ensure all groups expanded by default in More menu
- Wait for Hydration: Prevent flash of incorrect state
- Test on Real Devices: Responsive mode doesn't catch all mobile issues
- Clean Up Event Listeners: Prevent memory leaks in useEffect
- Memoize Computations: Use useMemo for expensive transformations
- Handle Nested Routes: Use startsWith for route matching
- Document Customizations: Track theme changes for consistency
Summary
This skill provides a standardized mobile bottom navbar with:
- ✅ Complete working code with accordion More menu pattern
- ✅ Visual reference screenshots showing exact UI
- ✅ 3-column icon grid for submenus and More menu
- ✅ Accordion sections in More menu (all expanded by default)
- ✅ Comprehensive documentation for all scenarios
- ✅ Multiple examples for different use cases
- ✅ Customization guides for branding
- ✅ Integration guides for auth systems
- ✅ Troubleshooting guide for common issues
Start with Quick Start section above, reference screenshots in references/screenshots/, and use the complete components from assets/ to ensure the correct UI pattern.