shadcn navbar: what shadcn ships, and an animated one

shadcn has no Navbar component. Here is what it ships instead (Navigation Menu, plus a Sheet for phones), when that free route is the better pick, and a Next.js navbar built around the Dynamic Island: page search, a live notification, and real links in the footer.

Dynamic Island Navbar, preview
One payment$3

By buying you agree to the Terms.

Ready forCursorv0Lovableshadcn CLI

Full product pageReact · Tailwind · TypeScript

Use it in Next.js

app/layout.tsx

import type { ReactNode } from "react";
import "./globals.css";
import { SiteNav } from "@/components/site-nav";
import { SiteFooter } from "@/components/site-footer";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body className="bg-background text-foreground antialiased">
        <SiteNav />
        {/* room for the island: 76 px tall at top 16 (60 px on phones) */}
        <main className="mx-auto max-w-5xl px-4 pt-28">{children}</main>
        <SiteFooter />
      </body>
    </html>
  );
}

components/site-nav.tsx

"use client";
import { useRouter } from "next/navigation";
import DynamicIsland from "@/components/DynamicIsland";
import { PAGES } from "@/lib/pages";

export function SiteNav() {
  const router = useRouter();

  return (
    // The island is absolutely positioned, so a fixed, zero-height header is enough.
    <header className="fixed inset-x-0 top-0 z-40">
      <DynamicIsland
        size="auto"
        top={16}
        tabs={["search", "notify"]}
        // accent goes into a CSS variable, so a shadcn theme token works here
        accent="var(--chart-2)"
        items={PAGES.map((page) => page.label)}
        resultTag="Page"
        searchPlaceholder="Search pages"
        avatar="A"
        notificationTitle="Deploy finished"
        notificationText="main is live on production"
        notificationAction="Open"
        onHome={() => router.push("/")}
        onSelect={(label) => {
          const page = PAGES.find((p) => p.label === label);
          if (page) router.push(page.href);
        }}
        onNotificationAction={() => router.push("/deploys")}
      />
    </header>
  );
}

lib/pages.ts

export const PAGES = [
  { label: "Dashboard", href: "/dashboard" },
  { label: "Deploys", href: "/deploys" },
  { label: "Billing", href: "/billing" },
  { label: "Team settings", href: "/settings/team" },
  { label: "API keys", href: "/settings/api-keys" },
  { label: "Docs", href: "/docs" },
];

components/site-footer.tsx

import Link from "next/link";
import { Button } from "@/components/ui/button";
import { PAGES } from "@/lib/pages";

// The island's search results are buttons, not links.
// Keep real links on every page for crawlers and for people who never open search.
export function SiteFooter() {
  return (
    <footer className="mt-24 border-t">
      <nav aria-label="Pages" className="mx-auto max-w-5xl px-4 py-6">
        <ul className="flex flex-wrap gap-x-2 gap-y-1">
          {PAGES.map((page) => (
            <li key={page.href}>
              <Button asChild variant="link" size="sm">
                <Link href={page.href}>{page.label}</Link>
              </Button>
            </li>
          ))}
        </ul>
      </nav>
    </footer>
  );
}

Good to know

  • What we ran on Oct 25 2026: the files on this page (the island snippet and the free marketing-nav.tsx) with the shipped DynamicIsland.tsx in a scratch Next.js 16.4.3 app with React 19.3.1, Tailwind CSS 4.3.3, radix-ui 1.8.1, framer-motion 14.6.0, lucide-react 1.61.0, shadcn 4.22.1 and TypeScript 7.0.2. The shadcn button, navigation-menu and sheet files were copied by hand from the new-york-v4 registry; we didn't run the CLI. tsc and next build passed. A wrong island tab name and a wrong Sheet side both failed tsc, so the props really are checked.
  • In headless Chromium at 1440 px and 390 px, typing "dep" in the island listed Deploys, picking it went to /deploys, the footer held six real links, and nothing scrolled sideways.
  • What we didn't run: a real phone, Safari or Firefox, a screen reader, dark mode, the Base UI version of Navigation Menu, or your own routes and theme. The pages in our run were placeholders.
  • The island's search results are buttons that call onSelect, not links. That's why the footer keeps a plain list of links with shadcn's Button asChild: crawlers follow those, and so do people who never open search.
  • The island's CSS names Space Grotesk and JetBrains Mono but doesn't load them. Load the same two fonts or change the names in dynamic-island.css to your theme's font. Otherwise it falls back to system-ui, as it did in our run.
  • On phones the open search box grows down over the top of your page. Keep the top padding on main (pt-28 above) so the closed island doesn't cover your heading.

shadcn navbar: there is no Navbar component, and what shadcn ships instead

  • shadcn's component list (ui.shadcn.com/llms.txt, read Oct 25 2026) has no Navbar and no Header. Under Layout and Navigation it lists Accordion, Breadcrumb, Navigation Menu, Sidebar, Tabs, Separator, Scroll Area and Resizable. Menubar is there too, but it's the File and Edit style bar from desktop apps, not a site header. The blocks page has three categories today: login, sidebar and signup.
  • The piece shadcn means for a site navbar is Navigation Menu, described in its docs as "A collection of links for navigating websites." You add it with shadcn@latest add navigation-menu. The Radix version of the docs says "Use the asChild prop to compose a custom link component such as Next.js Link"; the Base UI version does the same with a render prop.
  • People have asked for a navbar on GitHub. "[feat]: Navbar component" (#4006, June 2024) was closed two days later, and its one reply is a link to the Navigation Menu docs. "Feature Request: a responsive navbar with a dropdown menu" (#1942) was closed automatically for inactivity in February 2024.
  • npm, week of Oct 16 to 22, 2026: the shadcn CLI 4.22.1 (published Oct 18 2026) had 8,003,630 downloads, and @radix-ui/react-navigation-menu 1.4.1 (published Oct 12 2026), the Radix part under Navigation Menu, had 26,291,154. Its peer range runs from react ^16.8 to ^19.0.

Free shadcn navbar routes, and when they fit better

  • Navigation Menu plus a Sheet. Both are free and both come from the shadcn CLI. The file below puts Navigation Menu links in the header from md up and a Sheet ("Extends the Dialog component to display content that complements the main content of the screen.") behind a menu button on phones. For a marketing or docs site with a logo, a few links and maybe a dropdown or two, this is the better choice. It's free, it's shadcn's own parts, and it matches the rest of your UI. Use it and skip the island.
  • Third-party block sites. Searches for shadcn navbar also turn up block libraries such as shadcnblocks.com, whose navbar page listed 21+ sections on Oct 25 2026. Some blocks on these sites are free and some are paid; check the licence on the block you pick. We haven't used them and don't compare them here.
  • The island fits a different job: an app header where search is the main way around, plus one live status such as a deploy, an order or a message. It isn't a logo-and-links bar, it has no hamburger menu, and it has no dropdowns. Navigation Menu doesn't morph like this, and that's the only reason to reach for the island.

components/marketing-nav.tsx (the free route)

"use client";
import { useState } from "react";
import Link from "next/link";
import { MenuIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  NavigationMenu,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import {
  Sheet,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet";

const LINKS = [
  { label: "Features", href: "/features" },
  { label: "Docs", href: "/docs" },
  { label: "Blog", href: "/blog" },
  { label: "Changelog", href: "/changelog" },
];

// Free route: shadcn's Navigation Menu from md up, a Sheet below md.
export function MarketingNav() {
  const [open, setOpen] = useState(false);

  return (
    <header className="sticky top-0 z-40 border-b bg-background/80 backdrop-blur">
      <div className="mx-auto flex h-14 max-w-5xl items-center justify-between px-4">
        <Link href="/" className="font-semibold">
          Acme
        </Link>

        <NavigationMenu className="hidden md:flex">
          <NavigationMenuList>
            {LINKS.map((link) => (
              <NavigationMenuItem key={link.href}>
                <NavigationMenuLink asChild className={navigationMenuTriggerStyle()}>
                  <Link href={link.href}>{link.label}</Link>
                </NavigationMenuLink>
              </NavigationMenuItem>
            ))}
          </NavigationMenuList>
        </NavigationMenu>

        <Sheet open={open} onOpenChange={setOpen}>
          <SheetTrigger asChild>
            <Button variant="ghost" size="icon" className="md:hidden" aria-label="Open menu">
              <MenuIcon />
            </Button>
          </SheetTrigger>
          <SheetContent side="right">
            <SheetHeader>
              <SheetTitle>Menu</SheetTitle>
            </SheetHeader>
            <nav aria-label="Main" className="flex flex-col gap-1 px-4">
              {LINKS.map((link) => (
                <Button key={link.href} asChild variant="ghost" className="justify-start">
                  <Link href={link.href} onClick={() => setOpen(false)}>
                    {link.label}
                  </Link>
                </Button>
              ))}
            </nav>
          </SheetContent>
        </Sheet>
      </div>
    </header>
  );
}

shadcn navbar responsive: what Navigation Menu does on phones

  • Navigation Menu has no mobile mode of its own. shadcn's own demo (navigation-menu-demo in the new-york-v4 registry) hides three of its dropdown items below md with hidden md:block, lets the list wrap with flex-wrap, and sets viewport={isMobile}. So the official example drops items on phones rather than folding them into a menu button.
  • That's why the free file above uses a Sheet below md. In our run at 390 px the Navigation Menu was display none, the menu button opened the Sheet with all four links, and tapping Docs went to /docs and closed the Sheet. At 1440 px the links sat in the header and the button was hidden.
  • The island handles phones another way. With size="auto" it switches to its compact size below 640 px (232 px wide closed in our run, 60 px tall), and search takes the place of a menu. There's no brand slot on the left; the avatar prop takes any short node at the right end, so a logo mark or an initial fits there.

Dynamic Island in a shadcn app: Tailwind v4 and theme colours

  • The new-york-v4 registry files are written for Tailwind CSS v4 (the Navigation Menu demo uses classes like bg-linear-to-b and outline-hidden), and the registry's base CSS imports tw-animate-css and shadcn/tailwind.css. The Dynamic Island download is a Vite project set up with Tailwind CSS 3.4, React 18 and framer-motion ^11.11.0. You don't change your Tailwind version for it. The component styles itself in dynamic-island.css with qdi- classes, so Tailwind has nothing to compile for it.
  • Our Tailwind v4 check of every piece (Oct 25 2026, Tailwind 4.3.3 against 3.4.19) rated Dynamic Island a one-line fix: "The island label, subtitle and controls (font-sans on main and children, 4 elements) render in the system sans instead of Space Grotesk." Those elements are the demo stage in App.tsx, which gets font-sans from tailwind.config.js. The navbar on this page doesn't use the stage, so it needs nothing. If you keep the stage, skip @config for the zip's tailwind.config.js. In a shadcn app it either changes nothing (when your globals.css sets --font-sans in @theme inline) or switches your whole app's sans font to Space Grotesk, as we found on our shadcn login page. Scope one rule to the stage instead. Its main element has no class of its own, so add one next to font-sans (we used qdi-stage) and put this in app/globals.css: .qdi-stage { font-family: "Space Grotesk", system-ui, sans-serif; }. Load Space Grotesk with next/font or a link tag, because the rule only names it. We checked this on Oct 25 2026 in a scratch test page: the stage markup, dynamic-island.css and CSS compiled by Tailwind 4.3.3 with a shadcn-style --font-sans in @theme inline, read in headless Chromium. With @config the stage stayed on the theme font. With the scoped rule the stage and its headline switched to Space Grotesk, the body kept the theme font, and the island kept its own Space Grotesk stack. That test page used no shadcn components and was not a full Next.js build.
  • Colours: the accent prop goes straight into a CSS variable (--qdi-accent), so a shadcn theme token works. In our run accent="var(--chart-2)" coloured the active icon and the search match with the theme's chart colour. Pick a token that stands out on black. In shadcn's neutral light theme --primary is oklch(0.205 0 0), which is close to the island's own black.
  • React 19: the snippet type-checked against React 19.3.1 and @types/react 19.3.0, while the piece's package.json lists React 18. framer-motion is the island's one runtime dependency; we used 14.6.0, whose peer range is react ^18.0.0 || ^19.0.0.

Install

  1. Copy from the zipPut react/src/components into src/, then npm i framer-motion.
  2. Or ask your AIPaste PROMPT.md into Cursor, Lovable or v0.

Props

tabs
icons next to Home: "search", "music", "notify"
items / onSelect
what search finds, and the pick (push the route here)
onHome
the Home icon
size
"regular", "compact" or "auto" (compact under 640 px)
top
px from the top of the positioned parent
avatar
any short node at the right end
notificationTitle / notificationText / notificationAction
the bell state's text; onNotificationAction for its button
onStateChange
idle, notify, music or search
accent
any CSS colour, including a theme variable

How qodesign compares

All comparisons, with prices read on each library's own site.

Questions

Does shadcn have a navbar component?

No. shadcn's component list has Navigation Menu, Sidebar and Menubar but no Navbar or Header, and the blocks page has no navbar category. Navigation Menu is the part shadcn offers for a site header.

How do I make a shadcn navbar responsive?

Show Navigation Menu from md up and put the same links in a Sheet behind a menu button below md, as in marketing-nav.tsx above. shadcn's own demo hides some items below md instead of adding a menu button.

How do I use Next.js Link in a shadcn Navigation Menu?

With the Radix version, wrap Link in NavigationMenuLink asChild and give it navigationMenuTriggerStyle(). The Base UI version passes Link through the render prop.

Can the Dynamic Island use my shadcn theme colours?

Yes. The accent prop is set as a CSS variable, so accent="var(--chart-2)" or any other token works. Pick one that stands out on black.

Is the island a full replacement for a shadcn navbar?

Not for a logo-and-links header. It's built for search and one live status, with no dropdowns or hamburger menu. Keep real links on the page too, like the footer above.

Can Cursor or v0 add it for me?

Yes. Each download has a PROMPT.md. Paste it into Cursor, Lovable, v0 or Claude Code and it fits the piece into your app.

Can I use it in client work?

Yes, in personal and client projects. You can't resell the code itself as a kit.