Developer Docs
Quick reference guides with props tables, usage examples, and best practices for rapid development
NavigationHeader
Primary app navigation header with logo, mobile/desktop navigation, and hamburger/back button
NavigationHeader
Comprehensive responsive navigation header with desktop horizontal navigation, mobile bottom navigation bar, hamburger/back button, notification bell, user dropdown menu, and integrated navigation drawer.
💡 See It In Action
NavigationHeader is currently active at the top of this page! Try clicking the hamburger menu (☰) to open the navigation drawer, or view the navigation links in the header.
Live Example
Interactive NavigationHeader demo (scaled down to fit):
Demo Content Area
This is a scaled-down demo showing NavigationHeader functionality. Try clicking the hamburger menu or navigation links above.
Key Features
✓Desktop horizontal navigation with dropdown menus
✓Mobile fixed bottom navigation bar
✓Context-aware hamburger/back button
✓Notification bell with reactive badge count
✓User avatar with dropdown (Profile, Settings, Logout)
✓Active tab highlighting based on current route
✓HMR state persistence (survives hot reloads)
✓NavigationDrawer integration with shared state
🔧 Props Reference
All available props for the NavigationHeader component:
| Prop | Type | Default | Description |
|---|---|---|---|
| navLinks | Array | [] | Primary navigation links (visible on all screens, shown in mobile bottom nav) |
| dsktpNavLinks | Array | [] | Desktop-only links (visible at lg+ breakpoint in header) |
| navStructure | Object | null | Optional dropdown structure for complex navigation hierarchies |
| defaultTab | string | '' | Default active tab on mount |
| class | string | '' | Additional header CSS classes |
Nav Link Object Structure
| Prop | Type | Default | Description |
|---|---|---|---|
| path | string | required | Navigation path (e.g., "/services") |
| label | string | required | Display label for the link |
| icon | string | undefined | Oracle JET icon name (for mobile nav) |
| dropdown | Array | undefined | Optional dropdown items array |
⚠️ Return Value
IMPORTANT: NavigationHeader returns an array [drawer, header] - you must spread it!
// ✅ CORRECT - Spread the array
const app = div(
...NavigationHeader({
navLinks,
dsktpNavLinks
}),
main(/* page content */)
);
// ❌ WRONG - Don't nest in another element
const app = div(
div(NavigationHeader({ navLinks })), // WRONG!
main(/* page content */)
);💻 Basic Usage
Simple example showing how to integrate NavigationHeader into your app layout:
import { NavigationHeader } from 'shared-components/layout/NavigationHeader.js';
// Define navigation links
const navLinks = [
{ path: '/dashboard', label: 'Dashboard', icon: 'home' },
{ path: '/services', label: 'Services', icon: 'grid' },
{ path: '/messages', label: 'Messages', icon: 'message' },
{ path: '/profile', label: 'Profile', icon: 'person' }
];
// Desktop-only links
const dsktpNavLinks = [
{ path: '/discover', label: 'Discover' },
{ path: '/about', label: 'About' }
];
// Main app layout
export const App = () => {
return div(
// Spread NavigationHeader array
...NavigationHeader({
navLinks,
dsktpNavLinks
}),
// Main content
main(
{ class: 'container mx-auto p-4' },
// Page content here
)
);
};📋 With Dropdown Menus
Desktop navigation supports dropdown menus for complex hierarchies:
const dsktpNavLinks = [
{
path: '/components',
label: 'Components',
dropdown: [
{ path: '/components/forms', label: 'Forms', icon: 'edit' },
{ path: '/components/display', label: 'Display', icon: 'layout' },
{ path: '/components/feedback', label: 'Feedback', icon: 'bell' },
{ separator: true },
{ path: '/components/all', label: 'View All', icon: 'grid' }
]
},
{
path: '/framework',
label: 'Framework',
dropdown: [
{ path: '/framework/routing', label: 'Routing' },
{ path: '/framework/state', label: 'State Management' },
{ path: '/framework/styling', label: 'Styling' }
]
}
];
NavigationHeader({
navLinks,
dsktpNavLinks
})📱 Responsive Behavior
NavigationHeader adapts its layout and functionality based on screen size:
Desktop (lg+ breakpoint)
✓Horizontal navigation in header
✓navLinks + dsktpNavLinks both visible
✓Dropdowns appear below nav items
✓Logo in top-left
✓Notification bell + user avatar in top-right
Mobile (<lg breakpoint)
✓Fixed bottom navigation bar
✓Only navLinks visible (with icons)
✓Hamburger button opens drawer
✓Back button on subpages
✓Header shows logo + notifications + user avatar
🔄 Hamburger/Back Button Logic
The left button is context-aware and changes based on current state:
→ Shows hamburger icon
→ Click opens NavigationDrawer
→ Shows back arrow
→ Click navigates back in history
→ Shows X icon
→ Click closes NavigationDrawer
🎯 Active State Tracking
NavigationHeader automatically highlights the current active tab based on URL:
// Active state is determined by URL path matching
// If current URL is /services/123
// Then the /services nav link will be highlighted
// Active styles applied:
// - text-midnight (vs text-secondary)
// - border-sky (vs border-transparent)
// - Bottom border shows blue accent
// Works with nested routes:
// /services → highlights Services
// /services/new → highlights Services
// /services/123 → highlights Services👤 User Dropdown Menu
User avatar in top-right opens dropdown with:
✓Profile → /profile
✓Settings → /settings
✓Logout → Calls /api/v1/auth/logout, redirects to /auth/
Logout Flow
// Built-in logout handler:
// 1. Calls POST /api/v1/auth/logout
// 2. Stores previous login info in localStorage
// 3. Clears auth store
// 4. Redirects to /auth/loginas/ (or /auth/ if no previous login)
// Logout is automatic - no custom handler needed!🔔 Notification Bell
Shows notification badge when hasNotifications is true:
// Currently mock data (replace with real notifications):
const hasNotifications = van.state(true);
const notificationCount = van.state(3);
// Badge shows on bell icon when notifications exist
// Click handler is in NavigationHeader source (lines ~320-330)🔥 HMR State Persistence
NavigationHeader preserves state across hot module reloads (HMR):
// Global state store survives HMR
window.__navigationHeaderState = {
showDropdown: van.state(false),
isDrawerOpen: van.state(false),
activeDropdown: van.state(null)
};
// This means:
// - Open dropdown → save code → dropdown stays open
// - Open drawer → save code → drawer stays open
// - Improves development experience!✅ Best Practices
Follow these guidelines for optimal NavigationHeader implementation:
✓ Do This
✓Spread the returned array (...NavigationHeader)
✓Provide icons for mobile navigation
✓Limit navLinks to 4-5 items for mobile
✓Use dropdowns for complex hierarchies
✓Keep labels short (1-2 words)
✓Define navigation in layout.js
✓Test on mobile viewports
✗ Avoid This
✗Nest the returned array in another element
✗Omit mobile icons from navLinks
✗Overcrowd navigation (>5 items)
✗Use long labels that wrap on mobile
✗Hardcode navigation links
✗Modify global state directly
🔗 Integration with Router
NavigationHeader automatically integrates with the file-based router for navigation and active state tracking:
import { router, navigateTo } from 'utils/fileBasedRouterV2.js';
import { navigationPath } from 'utils/state.js';
import { NavigationHeader } from 'shared-components/layout/NavigationHeader.js';
// NavigationHeader automatically:
// 1. Listens to navigationPath for active state
// 2. Uses navigateTo() for navigation
// 3. Handles browser back/forward buttons
// 4. Updates active state on URL change
// Just provide the navigation structure:
const navLinks = [ /* your links */ ];
div(
...NavigationHeader({ navLinks }),
main(/* routed content */)
)📦 Complete Example
Full implementation example showing NavigationHeader with all features:
import van from 'vanjs-core';
import { NavigationHeader } from 'shared-components/layout/NavigationHeader.js';
const { div, main } = van.tags;
// Define navigation structure
const navLinks = [
{ path: '/', label: 'Home', icon: 'home' },
{ path: '/services', label: 'Services', icon: 'grid' },
{ path: '/messages', label: 'Messages', icon: 'message' },
{ path: '/profile', label: 'Profile', icon: 'person' }
];
const dsktpNavLinks = [
{
path: '/discover',
label: 'Discover',
dropdown: [
{ path: '/discover/services', label: 'Services' },
{ path: '/discover/neighbors', label: 'Neighbors' },
{ path: '/discover/events', label: 'Events' }
]
},
{ path: '/about', label: 'About' },
{ path: '/help', label: 'Help' }
];
// Main app layout
export const App = () => {
return div(
// Navigation (drawer + header)
...NavigationHeader({
navLinks,
dsktpNavLinks,
defaultTab: '/'
}),
// Main content area
main(
{ class: 'container mx-auto px-4 py-6 mb-20 lg:mb-0' },
// ↑ mb-20 for mobile bottom nav clearance
// Your routed content here
)
);
};🔧 Common Issues & Solutions
Solutions to common NavigationHeader integration problems:
Issue: Content hidden under header
// NavigationHeader is sticky top-0
// Add padding to main content:
main({ class: 'pt-16' }, /* content */)
// Or use mt-16 on first childIssue: Mobile nav covers bottom content
// Mobile bottom nav is fixed bottom-0
// Add margin-bottom to content:
main({ class: 'mb-20 lg:mb-0' }, /* content */)
// mb-20 on mobile, mb-0 on desktopIssue: Active state not updating
// NavigationHeader listens to window.location.pathname
// Ensure you're using navigateTo() from the router:
import { navigateTo } from 'utils/fileBasedRouterV2.js';
// Not window.location.href or <a href>
navigateTo('/services');Explore More Components
Browse other component categories