Navbar
NavigationA responsive marketing navbar with logo, links, auth + CTA buttons, and a mobile menu toggle.
1280pxOpen
components/blocks/navbar.tsx
"use client";
import * as React from "react";
import { Menu, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const links = [
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "Docs", href: "#" },
{ label: "Blog", href: "#" },
];
export function Navbar() {
const [open, setOpen] = React.useState(false);
return (
<header className="sticky top-0 z-40 border-b border-zinc-200 bg-zinc-50/80 text-zinc-950 backdrop-blur-md dark:border-zinc-800 dark:bg-zinc-950/80 dark:text-zinc-50">
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
<a
href="#"
className="flex items-center gap-2 font-semibold tracking-tight"
>
<span className="grid size-6 place-items-center rounded-md bg-zinc-950 text-xs font-bold text-zinc-50 dark:bg-zinc-50 dark:text-zinc-950">
A
</span>
Acme
</a>
<nav className="hidden items-center gap-1 text-sm md:flex">
{links.map((l) => (
<a
key={l.label}
href={l.href}
className="rounded-md px-3 py-1.5 text-zinc-500 transition-colors hover:text-zinc-950 dark:text-zinc-400 dark:hover:text-zinc-50"
>
{l.label}
</a>
))}
</nav>
<div className="hidden items-center gap-2 md:flex">
<Button variant="ghost" size="sm">
Sign in
</Button>
<Button size="sm">Get started</Button>
</div>
<button
type="button"
aria-label="Toggle menu"
aria-expanded={open}
onClick={() => setOpen((o) => !o)}
className="inline-flex size-9 items-center justify-center rounded-md text-zinc-950 md:hidden dark:text-zinc-50"
>
{open ? <X className="size-5" /> : <Menu className="size-5" />}
</button>
</div>
<div
className={cn(
"border-t border-zinc-200 md:hidden dark:border-zinc-800",
open ? "block" : "hidden",
)}
>
<nav className="mx-auto flex max-w-6xl flex-col gap-1 px-4 py-3 text-sm sm:px-6">
{links.map((l) => (
<a
key={l.label}
href={l.href}
className="rounded-md px-3 py-2 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-950 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-50"
>
{l.label}
</a>
))}
<div className="mt-2 flex flex-col gap-2">
<Button variant="outline" className="w-full">
Sign in
</Button>
<Button className="w-full">Get started</Button>
</div>
</nav>
</div>
</header>
);
}Installation
Terminal
npx shadcn@latest add https://ui.saumyarex.xyz/r/navbar.jsonInstalls the block and its component dependencies (button) in one step.
This block uses the button component — add it first.
Install dependencies
Terminal
npm install lucide-react clsx tailwind-mergeCopy the source
components/blocks/navbar.tsx
"use client";
import * as React from "react";
import { Menu, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const links = [
{ label: "Features", href: "#" },
{ label: "Pricing", href: "#" },
{ label: "Docs", href: "#" },
{ label: "Blog", href: "#" },
];
export function Navbar() {
const [open, setOpen] = React.useState(false);
return (
<header className="sticky top-0 z-40 border-b border-zinc-200 bg-zinc-50/80 text-zinc-950 backdrop-blur-md dark:border-zinc-800 dark:bg-zinc-950/80 dark:text-zinc-50">
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
<a
href="#"
className="flex items-center gap-2 font-semibold tracking-tight"
>
<span className="grid size-6 place-items-center rounded-md bg-zinc-950 text-xs font-bold text-zinc-50 dark:bg-zinc-50 dark:text-zinc-950">
A
</span>
Acme
</a>
<nav className="hidden items-center gap-1 text-sm md:flex">
{links.map((l) => (
<a
key={l.label}
href={l.href}
className="rounded-md px-3 py-1.5 text-zinc-500 transition-colors hover:text-zinc-950 dark:text-zinc-400 dark:hover:text-zinc-50"
>
{l.label}
</a>
))}
</nav>
<div className="hidden items-center gap-2 md:flex">
<Button variant="ghost" size="sm">
Sign in
</Button>
<Button size="sm">Get started</Button>
</div>
<button
type="button"
aria-label="Toggle menu"
aria-expanded={open}
onClick={() => setOpen((o) => !o)}
className="inline-flex size-9 items-center justify-center rounded-md text-zinc-950 md:hidden dark:text-zinc-50"
>
{open ? <X className="size-5" /> : <Menu className="size-5" />}
</button>
</div>
<div
className={cn(
"border-t border-zinc-200 md:hidden dark:border-zinc-800",
open ? "block" : "hidden",
)}
>
<nav className="mx-auto flex max-w-6xl flex-col gap-1 px-4 py-3 text-sm sm:px-6">
{links.map((l) => (
<a
key={l.label}
href={l.href}
className="rounded-md px-3 py-2 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-950 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-50"
>
{l.label}
</a>
))}
<div className="mt-2 flex flex-col gap-2">
<Button variant="outline" className="w-full">
Sign in
</Button>
<Button className="w-full">Get started</Button>
</div>
</nav>
</div>
</header>
);
}lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
/** Merge conditional class names and resolve Tailwind conflicts. */
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
/** Shared view-transition name so a card preview morphs into the detail
* page's preview. Must match on both ends; unique per registry entry. */
export function previewTransitionName(name: string) {
return `preview-${name}`;
}