Header Component
Navigation header with logo, links, and CTA buttons
Header Component
The <Header> component provides site navigation with logo, links, and call-to-action buttons. It includes responsive mobile menu, scroll effects, and customizable styling.
Interface
import type { HeaderProps } from 'mdxui'
interface HeaderLink {
label: string
href: string
}
interface HeaderProps {
/** Logo element */
logo: ReactNode
/** Navigation links */
nav: NavItem[]
/** Primary CTA button */
cta?: CTA
/** Custom link component (e.g., Next.js Link) */
LinkComponent?: ComponentType<HeaderLinkProps>
}Usage
Basic Example
import { Header } from '@mdxui/beacon'
<Header
logo={<Logo />}
nav={[
{ label: "Features", href: "/features" },
{ label: "Pricing", href: "/pricing" },
{ label: "Docs", href: "/docs" },
]}
cta={{ label: "Sign Up", href: "/signup" }}
/>With Custom Link Component
For Next.js or React Router:
import Link from 'next/link'
<Header
logo={<Logo />}
nav={navLinks}
LinkComponent={Link}
/>Full Example
<Header
logo={<Logo src="/logo.svg" alt="Acme" />}
nav={[
{ label: "Features", href: "#features" },
{ label: "Pricing", href: "#pricing" },
{ label: "Docs", href: "/docs" },
{ label: "Blog", href: "/blog" },
]}
cta={{ label: "Get Started", href: "/signup" }}
/>Props
logo (required)
Type: ReactNode
Logo element or component displayed on the left side of the header.
// Image logo
<Header logo={<img src="/logo.svg" alt="Acme" />} />
// Component logo
<Header logo={<Logo size="lg" />} />
// Text logo
<Header logo={<span className="font-bold text-xl">Acme</span>} />nav (required)
Type: NavItem[]
Array of navigation links displayed in the center (desktop) or mobile menu.
interface NavItem {
label: string
href: string
}
<Header
nav={[
{ label: "Features", href: "#features" },
{ label: "Pricing", href: "#pricing" },
{ label: "About", href: "/about" },
]}
/>cta (optional)
Type: CTA
Primary call-to-action button displayed on the right side.
interface CTA {
label: string
href: string
variant?: 'default' | 'outline' | 'ghost'
}
<Header
cta={{
label: "Sign Up",
href: "/signup",
variant: "default"
}}
/>LinkComponent (optional)
Type: ComponentType<HeaderLinkProps>
Custom link component for routing (e.g., Next.js Link, React Router Link).
import Link from 'next/link'
<Header LinkComponent={Link} />Interface:
interface HeaderLinkProps {
href: string
children: ReactNode
className?: string
onClick?: () => void
}Implementation Details
The reference implementation from @mdxui/beacon includes:
Features
- Responsive Mobile Menu - Hamburger menu for mobile/tablet
- Scroll Effects - Background blur and border on scroll
- Sticky Position - Fixed to top of viewport
- Smooth Animations - CSS transitions for all state changes
- Body Scroll Lock - Prevents scrolling when mobile menu is open
Structure
<header className="fixed inset-x-0 top-0 z-50">
<div className="container">
<div className="flex items-center justify-between">
{/* Logo */}
<LinkComponent href="/">
{logo}
</LinkComponent>
{/* Desktop Navigation (center) */}
<NavigationMenu>
{nav.map(link => (
<NavigationMenuItem>
<LinkComponent href={link.href}>
{link.label}
</LinkComponent>
</NavigationMenuItem>
))}
</NavigationMenu>
{/* Mobile Menu Button */}
<button onClick={toggleMobileMenu}>
<Menu />
</button>
{/* Desktop CTA */}
{cta && (
<Button variant={cta.variant}>
{cta.label}
</Button>
)}
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<nav>
{nav.map(link => (
<LinkComponent href={link.href}>
{link.label}
</LinkComponent>
))}
</nav>
)}
</div>
</header>Scroll Behavior
The header adds a backdrop blur and border on scroll:
const [isScrolled, setIsScrolled] = useState(false)
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 50)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])Styles:
<header
data-scrolled={isScrolled}
className="
in-data-scrolled:bg-background/75
in-data-scrolled:backdrop-blur
in-data-scrolled:border-b
"
>Variants
With Secondary CTA
Some implementations support both primary and secondary CTAs:
<Header
logo={<Logo />}
nav={navLinks}
primaryCta="Sign Up"
onPrimaryCta={() => router.push('/signup')}
secondaryCta="Log In"
onSecondaryCta={() => router.push('/login')}
/>With Max Width
Control the container width:
<Header
logo={<Logo />}
nav={navLinks}
maxWidth="max-w-7xl" // or 'max-w-5xl', 'max-w-6xl'
/>Custom Styling
Pass additional classes:
<Header
logo={<Logo />}
nav={navLinks}
className="bg-transparent border-none"
/>Real-World Examples
Marketing Site Header
const marketingNav = [
{ label: "Features", href: "#features" },
{ label: "Pricing", href: "#pricing" },
{ label: "Customers", href: "#customers" },
{ label: "Blog", href: "/blog" },
]
<Header
logo={<Logo variant="dark" />}
nav={marketingNav}
cta={{ label: "Start Free Trial", href: "/signup" }}
/>Documentation Header
const docsNav = [
{ label: "Docs", href: "/docs" },
{ label: "API", href: "/docs/api" },
{ label: "Examples", href: "/examples" },
{ label: "GitHub", href: "https://github.com/..." },
]
<Header
logo={<Logo />}
nav={docsNav}
cta={{ label: "Dashboard", href: "/app" }}
/>Minimal Header
<Header
logo={<Logo />}
nav={[
{ label: "About", href: "/about" },
{ label: "Contact", href: "/contact" },
]}
/>Accessibility
The reference implementation includes:
- Semantic HTML - Uses
<header>,<nav>, and<button>elements - ARIA Labels - Mobile menu toggle has
aria-label - Keyboard Navigation - Full keyboard support via Radix NavigationMenu
- Focus Management - Visible focus indicators
- Screen Reader Support - Proper labeling for assistive tech
<button
type="button"
onClick={toggleMenu}
aria-label={isMobileMenuOpen ? 'Close Menu' : 'Open Menu'}
aria-expanded={isMobileMenuOpen}
>
<Menu />
</button>Composition with Site
The <Header> component is typically rendered by <Site>:
<Site name="Acme">
{/* Site automatically renders Header */}
<LandingPage>...</LandingPage>
</Site>How it works:
- The
<Site>component includes<Header>automatically - Header props are configured in the template implementation
- You can override by passing custom Header component
Mobile Menu Behavior
Open State
When the mobile menu is open:
- Body scroll is disabled
- Menu slides in from top
- Backdrop blur is applied
- Close icon (X) replaces hamburger icon
Close Triggers
The mobile menu closes when:
- User clicks the close button
- User navigates to a link
- User clicks outside the menu
- Screen resizes to desktop
Implementation
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
// Disable body scroll when menu is open
useEffect(() => {
if (isMobileMenuOpen) {
document.body.style.overflow = 'hidden'
}
return () => {
document.body.style.overflow = ''
}
}, [isMobileMenuOpen])
// Close menu on navigation
const closeMenu = () => setIsMobileMenuOpen(false)
{nav.map(link => (
<LinkComponent
href={link.href}
onClick={closeMenu}
>
{link.label}
</LinkComponent>
))}TypeScript
Full type definitions:
import type { ReactNode, ComponentType } from 'react'
import type { z } from 'zod'
export const NavItemSchema = z.object({
label: z.string(),
href: z.string(),
})
export const CTASchema = z.object({
label: z.string(),
href: z.string(),
variant: z.enum(['default', 'outline', 'ghost']).optional(),
})
export const HeaderPropsSchema = z.object({
logo: z.any(),
nav: z.array(NavItemSchema),
cta: CTASchema.optional(),
LinkComponent: z.any().optional(),
})
export type NavItem = z.infer<typeof NavItemSchema>
export type CTA = z.infer<typeof CTASchema>
export type HeaderProps = z.infer<typeof HeaderPropsSchema>
export type Header = Component<HeaderProps>Related Components
- Site Component - Root wrapper (renders Header)
- Footer Component - Site footer
- NavigationMenu - Base navigation component