Welcome to Acme Inc
This demo showcases all navbar features: dropdowns, badges, sticky positioning, and responsive mobile menu.
Section 1
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 2
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 3
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 4
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 5
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 6
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 7
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 8
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 9
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Section 10
Scroll down to see the sticky header behavior. The navbar stays at the top with a solid background and shadow for better visibility.
Features
- ✅ Sticky positioning - Stays at top while scrolling
- ✅ Solid design - Clean solid colors with shadows
- ✅ Dropdown menus - Hover dropdowns on desktop
- ✅ Mobile responsive - Hamburger menu for mobile
- ✅ Badge support - Add "New", "Beta", or custom badges to links
- ✅ CTA buttons - Primary and secondary action buttons
- ✅ Custom logo - Support for logo image or text
- ✅ Underline animation - Smooth underline on hover
- ✅ Smooth animations - Transitions and hover effects
- ✅ TypeScript support - Full type safety
- ✅ Accessible - Keyboard navigation and ARIA labels
Installation
Copy and paste the following code into your project.
import { Menu, X, ChevronDown } from "lucide-react";
import { useState } from "react";
interface NavLink {
label: string;
href: string;
badge?: string;
}
interface NavDropdown {
label: string;
items: NavLink[];
}
interface HeaderNavbarProps {
logo?: React.ReactNode;
logoText?: string;
links?: (NavLink | NavDropdown)[];
ctaButton?: {
label: string;
href: string;
variant?: "primary" | "secondary";
};
secondaryButton?: {
label: string;
href: string;
};
}
const isDropdown = (item: NavLink | NavDropdown): item is NavDropdown => {
return "items" in item;
};
export function HeaderNavbar({
logo,
logoText = "Brand",
links = [],
ctaButton,
secondaryButton,
}: HeaderNavbarProps) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
return (
<header className="sticky top-0 z-50 w-full border-b border-zinc-800 bg-zinc-950 shadow-lg">
<nav className="container mx-auto px-4 md:px-6">
<div className="flex h-16 items-center justify-between">
{/* Logo */}
<a
href="/"
className="flex items-center gap-3 transition-transform hover:scale-105"
>
{logo || (
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-blue-600 shadow-lg shadow-blue-600/30">
<span className="text-base font-bold text-white">
{logoText.charAt(0)}
</span>
</div>
)}
<span className="text-xl font-bold text-white">{logoText}</span>
</a>
{/* Desktop Navigation */}
<div className="hidden items-center gap-8 lg:flex">
{links.map((item, index) => {
if (isDropdown(item)) {
return (
<div
key={index}
className="group relative"
onMouseEnter={() => setOpenDropdown(item.label)}
onMouseLeave={() => setOpenDropdown(null)}
>
<button className="flex items-center gap-1.5 text-sm font-medium text-zinc-300 transition-colors hover:text-white">
{item.label}
<ChevronDown className="h-4 w-4 transition-transform duration-200 group-hover:rotate-180" />
</button>
{openDropdown === item.label && (
<div className="absolute left-0 top-full pt-3">
<div className="min-w-[220px] rounded-xl border border-zinc-800 bg-zinc-900 p-2 shadow-2xl">
{item.items.map((subItem, subIndex) => (
<a
key={subIndex}
href={subItem.href}
className="flex items-center justify-between rounded-lg px-4 py-3 text-sm text-zinc-300 transition-all hover:bg-zinc-800 hover:text-white"
>
<span className="font-medium">
{subItem.label}
</span>
{subItem.badge && (
<span className="rounded-full bg-blue-500 px-2.5 py-0.5 text-xs font-semibold text-white">
{subItem.badge}
</span>
)}
</a>
))}
</div>
</div>
)}
</div>
);
}
return (
<a
key={index}
href={item.href}
className="group relative text-sm font-medium text-zinc-300 transition-colors hover:text-white"
>
<span className="relative">
{item.label}
<span className="absolute -bottom-1 left-0 h-0.5 w-0 bg-blue-500 transition-all duration-300 group-hover:w-full" />
</span>
{item.badge && (
<span className="ml-2 rounded-full bg-blue-500 px-2.5 py-0.5 text-xs font-semibold text-white">
{item.badge}
</span>
)}
</a>
);
})}
</div>
{/* Desktop CTA Buttons */}
<div className="hidden items-center gap-4 lg:flex">
{secondaryButton && (
<a
href={secondaryButton.href}
className="rounded-lg px-5 py-2.5 text-sm font-semibold text-zinc-300 transition-all hover:text-white"
>
{secondaryButton.label}
</a>
)}
{ctaButton && (
<a
href={ctaButton.href}
className={`rounded-lg px-5 py-2.5 text-sm font-semibold shadow-lg transition-all ${
ctaButton.variant === "secondary"
? "border-2 border-zinc-700 bg-zinc-800 text-white hover:border-zinc-600 hover:bg-zinc-700"
: "bg-blue-600 text-white shadow-blue-600/30 hover:bg-blue-500 hover:shadow-blue-600/50"
}`}
>
{ctaButton.label}
</a>
)}
</div>
{/* Mobile Menu Button */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="rounded-lg bg-zinc-900 p-2.5 text-zinc-300 transition-all hover:bg-zinc-800 hover:text-white lg:hidden"
aria-label="Toggle menu"
>
{mobileMenuOpen ? (
<X className="h-5 w-5" />
) : (
<Menu className="h-5 w-5" />
)}
</button>
</div>
{/* Mobile Menu */}
{mobileMenuOpen && (
<div className="border-t border-zinc-800 bg-zinc-900/50 py-4 lg:hidden">
<div className="space-y-1">
{links.map((item, index) => {
if (isDropdown(item)) {
return (
<div key={index} className="space-y-1">
<button
onClick={() =>
setOpenDropdown(
openDropdown === item.label ? null : item.label,
)
}
className="flex w-full items-center justify-between rounded-lg px-4 py-2.5 text-sm font-medium text-zinc-400 transition-colors hover:bg-zinc-900 hover:text-white"
>
{item.label}
<ChevronDown
className={`h-4 w-4 transition-transform ${
openDropdown === item.label ? "rotate-180" : ""
}`}
/>
</button>
{openDropdown === item.label && (
<div className="ml-4 space-y-1 border-l border-zinc-800 pl-4">
{item.items.map((subItem, subIndex) => (
<a
key={subIndex}
href={subItem.href}
className="flex items-center justify-between rounded-lg px-4 py-2 text-sm text-zinc-400 transition-colors hover:bg-zinc-900 hover:text-white"
>
<span>{subItem.label}</span>
{subItem.badge && (
<span className="rounded-full bg-blue-500/10 px-2 py-0.5 text-xs font-medium text-blue-400">
{subItem.badge}
</span>
)}
</a>
))}
</div>
)}
</div>
);
}
return (
<a
key={index}
href={item.href}
className="flex items-center justify-between rounded-lg px-4 py-2.5 text-sm font-medium text-zinc-400 transition-colors hover:bg-zinc-900 hover:text-white"
>
<span>{item.label}</span>
{item.badge && (
<span className="rounded-full bg-blue-500/10 px-2 py-0.5 text-xs font-medium text-blue-400">
{item.badge}
</span>
)}
</a>
);
})}
</div>
{/* Mobile CTA Buttons */}
<div className="mt-4 space-y-3 border-t border-zinc-800 pt-4">
{secondaryButton && (
<a
href={secondaryButton.href}
className="block rounded-lg border-2 border-zinc-700 bg-zinc-800 px-4 py-3 text-center text-sm font-semibold text-white transition-all hover:border-zinc-600 hover:bg-zinc-700"
>
{secondaryButton.label}
</a>
)}
{ctaButton && (
<a
href={ctaButton.href}
className="block rounded-lg bg-blue-600 px-4 py-3 text-center text-sm font-semibold text-white shadow-lg shadow-blue-600/30 transition-all hover:bg-blue-500 hover:shadow-blue-600/50"
>
{ctaButton.label}
</a>
)}
</div>
</div>
)}
</nav>
</header>
);
}Usage
Basic Usage
import HeaderNavbar from "@/components/ui/header-navbar";
export default function Page() {
return (
<HeaderNavbar
logoText="Brand"
links={[
{ label: "Home", href: "/" },
{ label: "About", href: "/about" },
{ label: "Pricing", href: "/pricing" },
]}
ctaButton={{
label: "Get Started",
href: "/signup",
}}
/>
);
}With Dropdown Menus
<HeaderNavbar
logoText="Acme Inc"
links={[
{ label: "Home", href: "/" },
{
label: "Products",
items: [
{ label: "Analytics", href: "/products/analytics" },
{ label: "Marketing", href: "/products/marketing" },
{ label: "Commerce", href: "/products/commerce" },
],
},
{ label: "Pricing", href: "/pricing" },
]}
ctaButton={{
label: "Get Started",
href: "/signup",
}}
/>With Badges
<HeaderNavbar
logoText="Brand"
links={[
{ label: "Home", href: "/" },
{ label: "Features", href: "/features", badge: "New" },
{
label: "Products",
items: [
{ label: "Analytics", href: "/analytics" },
{ label: "AI Tools", href: "/ai", badge: "Beta" },
],
},
]}
/>With Custom Logo
<HeaderNavbar
logo={
<img
src="/logo.svg"
alt="Logo"
className="h-8 w-8"
/>
}
logoText="Company"
links={[...]}
/>With Secondary Button
<HeaderNavbar
logoText="Brand"
links={[...]}
secondaryButton={{
label: "Sign In",
href: "/signin",
}}
ctaButton={{
label: "Get Started",
href: "/signup",
}}
/>Secondary CTA Variant
<HeaderNavbar
logoText="Brand"
links={[...]}
ctaButton={{
label: "Contact Sales",
href: "/contact",
variant: "secondary", // Outlined style
}}
/>Props
HeaderNavbarProps
| Prop | Type | Default | Description |
|---|---|---|---|
logo | ReactNode | undefined | Custom logo component |
logoText | string | "Brand" | Logo text |
links | (NavLink | NavDropdown)[] | [] | Navigation links |
ctaButton | CTAButton | undefined | Primary CTA button |
secondaryButton | SecondaryButton | undefined | Secondary button |
NavLink
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | Required | Link text |
href | string | Required | Link URL |
badge | string | undefined | Optional badge text |
NavDropdown
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | Required | Dropdown label |
items | NavLink[] | Required | Dropdown items |
CTAButton
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | Required | Button text |
href | string | Required | Button URL |
variant | "primary" | "secondary" | "primary" | Button style |
TypeScript Interface
interface HeaderNavbarProps {
logo?: React.ReactNode;
logoText?: string;
links?: (NavLink | NavDropdown)[];
ctaButton?: {
label: string;
href: string;
variant?: "primary" | "secondary";
};
secondaryButton?: {
label: string;
href: string;
};
}
interface NavLink {
label: string;
href: string;
badge?: string;
}
interface NavDropdown {
label: string;
items: NavLink[];
}Styling
The navbar uses:
- Sticky positioning -
sticky top-0with shadow - Solid colors -
bg-zinc-950with solid borders - Dark theme - Zinc-950 background with zinc-800 borders
- Blue CTA - Solid blue-600 button with hover effects
- Underline animation - Blue underline on nav link hover
- Smooth transitions - All hover states animated
- Shadow effects - Subtle shadows on logo and buttons
Responsive Behavior
- Desktop (lg+): Horizontal menu with hover dropdowns
- Mobile: Hamburger menu with expandable sections
- Tablet: Adapts based on screen size
Accessibility
- Keyboard navigation support
- ARIA labels on interactive elements
- Focus states on all clickable items
- Semantic HTML structure
Use Cases
Perfect for:
- SaaS application headers
- Marketing website navigation
- Product landing pages
- Documentation sites
- E-commerce platforms
- Portfolio websites
- Corporate websites
- Startup landing pages