Skip to main content
Navigation is the top-level navigation bar used by Layout. It renders a dark header with a brand logo link and three route links — /, /features, and /about — using React Router for client-side navigation.

Source

src/components/Navigation.tsx
import { NavLink, Link } from 'react-router-dom';
import { Button } from '@/components/ui/button';

export function Navigation() {
    return (
        <header className="bg-gray-800 text-white p-4 shadow-lg">
            <nav className="container mx-auto flex flex-wrap items-center justify-between gap-2">
                <Link to="/" className="text-xl font-bold hover:text-gray-300 transition-colors shrink-0">
                    Vite Template
                </Link>
                <div className="flex flex-wrap gap-1 md:gap-2 w-full md:w-auto">
                    {[
                        { to: '/', label: 'Home' },
                        { to: '/features', label: 'Features' },
                        { to: '/about', label: 'About' },
                    ].map((i) => (
                        <NavLink key={i.to} to={i.to} className={({ isActive }) =>
                            `inline-block rounded-md ${isActive ? 'bg-white/10' : ''}`
                        }>
                            {({ isActive }) => (
                                <Button variant="ghost" className={`text-white hover:bg-gray-700 ${isActive ? 'bg-white/10' : ''}`}>
                                    {i.label}
                                </Button>
                            )}
                        </NavLink>
                    ))}
                </div>
            </nav>
        </header>
    );
}

How it works

The component uses two different React Router APIs:
  • Link — for the brand logo. A plain client-side link, no active-state styling needed.
  • NavLink — for the route links. NavLink exposes an isActive boolean in its render function and className callback, which the component uses to apply bg-white/10 highlight to the currently active route.
Each route link renders a Button with variant="ghost" and text-white so it appears as a white text button on the dark bg-gray-800 header.

Routes covered

LabelPathPage component
Home/HomePage
Features/featuresFeaturesPage
About/aboutAboutPage
These paths correspond exactly to the child routes defined in src/routes/AppRoutes.tsx.

Relationship with AppRoutes

Navigation links and route definitions must stay in sync:
  • Every path in AppRoutes.tsx that you want users to reach from the nav needs a corresponding entry in the Navigation link array.
  • Every path in the array must point to a route that exists in AppRoutes.tsx.
Navigation is a separate component (rather than inline JSX in Layout) to keep concerns separated. The layout shell handles structure; the navigation component handles which links to show. When you add a new route, add a matching entry to the navigation array in Navigation.tsx.
1

Add the route in AppRoutes.tsx

Register your new page as a child route:
src/routes/AppRoutes.tsx
{ path: 'contact', element: <ContactPage /> },
2

Add the link in Navigation.tsx

Add a new entry to the array inside the .map() call:
src/components/Navigation.tsx
{[
    { to: '/', label: 'Home' },
    { to: '/features', label: 'Features' },
    { to: '/about', label: 'About' },
    { to: '/contact', label: 'Contact' }, 
].map((i) => (
    // ...existing NavLink render
))}
Use the same path string in both files. A mismatch means the nav link will navigate to a route that renders nothing.

How to customize

Change the header background

Replace bg-gray-800 on the <header> element with any Tailwind color:
<header className="bg-indigo-900 text-white p-4 shadow-lg">
The active-state highlight is set via the isActive flag in both the NavLink className callback and the inner Button className. Update bg-white/10 and hover:bg-gray-700 to match your design:
<NavLink key={i.to} to={i.to} className={({ isActive }) =>
    `inline-block rounded-md ${isActive ? 'bg-indigo-500' : ''}`
}>
    {({ isActive }) => (
        <Button variant="ghost" className={`text-white hover:bg-indigo-600 ${isActive ? 'bg-indigo-500' : ''}`}>
            {i.label}
        </Button>
    )}
</NavLink>
Pair each label with an icon from lucide-react, which is already a dependency in this template:
import { Home, Zap, Info } from 'lucide-react';

const links = [
    { to: '/', label: 'Home', icon: Home },
    { to: '/features', label: 'Features', icon: Zap },
    { to: '/about', label: 'About', icon: Info },
];

// Inside the map:
<Button variant="ghost" className="text-white hover:bg-gray-700 flex items-center gap-2">
    <i.icon size={16} />
    {i.label}
</Button>

Move Navigation to a sidebar

Navigation is rendered as a direct child of the outer flex-col container in Layout. To move it to a sidebar, change the outer container to flex-row in Layout.tsx and adjust the nav’s width and height styles:
src/components/Layout.tsx
<div className="min-h-screen flex flex-row bg-gray-50">
    <Navigation />  {/* now renders as a left sidebar */}
    <main className="grow p-4 md:p-8">
        <Outlet />
    </main>
</div>

Build docs developers (and LLMs) love