The Header component provides a fully-featured navigation system with responsive mobile menu, sticky scrolling behavior, dropdown submenus, and integrated dark mode toggle. It’s the primary navigation element used across all pages.
Features
Responsive mobile hamburger menu
Sticky header on scroll
Dropdown submenu support
Dark mode toggle integration
Next.js Link integration for client-side routing
Active route highlighting
Smooth transitions and animations
Logo switching between light/dark modes
Basic Usage
import Header from "@/components/Header" ;
export default function Layout ({ children }) {
return (
<>
< Header />
< main > { children } </ main >
</>
);
}
The Header component uses the "use client" directive because it manages state for the mobile menu, sticky behavior, and submenus.
Menu items are defined in src/components/Header/menuData.tsx:
import { Menu } from "@/types/menu" ;
const menuData : Menu [] = [
{
id: 1 ,
title: "Home" ,
path: "/" ,
newTab: false ,
},
{
id: 2 ,
title: "About" ,
path: "/about" ,
newTab: false ,
},
{
id: 3 ,
title: "Pages" ,
newTab: false ,
submenu: [
{
id: 31 ,
title: "Blog Page" ,
path: "/blog" ,
newTab: false ,
},
{
id: 32 ,
title: "Sign In" ,
path: "/signin" ,
newTab: false ,
},
],
},
];
export default menuData ;
The Menu type is defined in src/types/menu.ts:
export type Menu = {
id : number ;
title : string ;
path ?: string ;
newTab : boolean ;
submenu ?: Menu [];
};
Unique identifier for the menu item
Display text for the menu item
Navigation path. Omit for parent items with submenus.
Whether to open the link in a new tab (currently not implemented for internal links)
Array of nested menu items for dropdown menus
Customization
Add new menu items to menuData.tsx:
src/components/Header/menuData.tsx
const menuData : Menu [] = [
{
id: 1 ,
title: "Home" ,
path: "/" ,
newTab: false ,
},
{
id: 2 ,
title: "Services" ,
path: "/services" ,
newTab: false ,
},
{
id: 3 ,
title: "Portfolio" ,
path: "/portfolio" ,
newTab: false ,
},
{
id: 4 ,
title: "Contact" ,
path: "/contact" ,
newTab: false ,
},
];
Add submenu items by omitting path and adding a submenu array:
{
id : 5 ,
title : "Resources" ,
newTab : false ,
submenu : [
{
id: 51 ,
title: "Documentation" ,
path: "/docs" ,
newTab: false ,
},
{
id: 52 ,
title: "Blog" ,
path: "/blog" ,
newTab: false ,
},
{
id: 53 ,
title: "FAQ" ,
path: "/faq" ,
newTab: false ,
},
],
}
Changing Logos
Update the logo images in the Header component:
src/components/Header/index.tsx
< Link href = "/" className = { `header-logo block w-full ${ sticky ? "py-5 lg:py-2" : "py-8" } ` } >
< Image
src = "/images/logo/your-logo.svg"
alt = "logo"
width = { 140 }
height = { 30 }
className = "w-full dark:hidden"
/>
< Image
src = "/images/logo/your-logo-dark.svg"
alt = "logo"
width = { 140 }
height = { 30 }
className = "hidden w-full dark:block"
/>
</ Link >
Sticky Behavior
The header becomes sticky when scrolling past 80px:
const handleStickyNavbar = () => {
if ( window . scrollY >= 80 ) {
setSticky ( true );
} else {
setSticky ( false );
}
};
To change the threshold:
if ( window . scrollY >= 120 ) { // Trigger at 120px instead
setSticky ( true );
}
The header has different styles based on sticky state:
< header
className = { `header left-0 top-0 z-40 flex w-full items-center ${
sticky
? "dark:bg-gray-dark dark:shadow-sticky-dark fixed z-[9999] bg-white !bg-opacity-80 shadow-sticky backdrop-blur-sm transition"
: "absolute bg-transparent"
} ` }
>
Normal state : Absolute positioning with transparent background
Sticky state : Fixed positioning with white background, shadow, and backdrop blur
The header includes Sign In and Sign Up buttons:
< div className = "flex items-center justify-end pr-16 lg:pr-0" >
< Link
href = "/signin"
className = "hidden px-7 py-3 text-base font-medium text-dark hover:opacity-70 dark:text-white md:block"
>
Sign In
</ Link >
< Link
href = "/signup"
className = "ease-in-up shadow-btn hover:shadow-btn-hover hidden rounded-sm bg-primary px-8 py-3 text-base font-medium text-white transition duration-300 hover:bg-opacity-90 md:block md:px-9 lg:px-6 xl:px-9"
>
Sign Up
</ Link >
< div >
< ThemeToggler />
</ div >
</ div >
To hide these buttons:
{ /* Remove or comment out Sign In/Sign Up links */ }
< div className = "flex items-center justify-end pr-16 lg:pr-0" >
< ThemeToggler />
</ div >
The mobile menu is controlled by state:
const [ navbarOpen , setNavbarOpen ] = useState ( false );
const navbarToggleHandler = () => {
setNavbarOpen ( ! navbarOpen );
};
The hamburger icon animates into an X when opened:
< button
onClick = { navbarToggleHandler }
id = "navbarToggler"
aria-label = "Mobile Menu"
className = "absolute right-4 top-1/2 block translate-y-[-50%] rounded-lg px-3 py-[6px] ring-primary focus:ring-2 lg:hidden"
>
< span className = { `relative my-1.5 block h-0.5 w-[30px] bg-black transition-all duration-300 dark:bg-white ${ navbarOpen ? " top-[7px] rotate-45" : " " } ` } />
< span className = { `relative my-1.5 block h-0.5 w-[30px] bg-black transition-all duration-300 dark:bg-white ${ navbarOpen ? "opacity-0 " : " " } ` } />
< span className = { `relative my-1.5 block h-0.5 w-[30px] bg-black transition-all duration-300 dark:bg-white ${ navbarOpen ? " top-[-8px] -rotate-45" : " " } ` } />
</ button >
Active Route Highlighting
Menu items are highlighted when on the current page:
const usePathName = usePathname ();
< Link
href = { menuItem . path }
className = { `flex py-2 text-base lg:mr-0 lg:inline-flex lg:px-0 lg:py-6 ${
usePathName === menuItem . path
? "text-primary dark:text-white"
: "text-dark hover:text-primary dark:text-white/70 dark:hover:text-white"
} ` }
>
{ menuItem . title }
</ Link >
Dark Mode Toggle
The header includes the ThemeToggler component. See the ThemeToggler documentation for customization options.
Create deeply nested menu structures:
const menuData : Menu [] = [
{
id: 1 ,
title: "Products" ,
newTab: false ,
submenu: [
{
id: 11 ,
title: "Software" ,
newTab: false ,
submenu: [
{
id: 111 ,
title: "Web Apps" ,
path: "/products/software/web" ,
newTab: false ,
},
{
id: 112 ,
title: "Mobile Apps" ,
path: "/products/software/mobile" ,
newTab: false ,
},
],
},
{
id: 12 ,
title: "Hardware" ,
path: "/products/hardware" ,
newTab: false ,
},
],
},
];
The current implementation only supports one level of submenus. For multi-level menus, you’ll need to modify the Header component to recursively render nested submenus.
Breakpoints
The header is responsive across all screen sizes:
Mobile (< 1024px): Hamburger menu
Desktop (>= 1024px): Full horizontal navigation
Accessibility
The mobile menu toggle has aria-label="Mobile Menu"
Keyboard navigation is fully supported
Focus states are visible with focus:ring-2
Semantic HTML with <header>, <nav>, and <ul> elements
Z-Index Layers
Normal header: z-40
Sticky header: z-[9999]
Mobile menu dropdown: z-30
Ensure other components don’t interfere with these layers.
Footer Bottom navigation and site information
ScrollUp Scroll to top button for better navigation