# Navbar Section v1

> Sticky blurred header with optional lorem-demo below the install page for scroll testing.

A shadcn/ui block from the [@uiception](https://uiception.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/navbar-section-v1).

- Page, with a live preview: https://shadcn.exchange/r/uiception/navbar-section-v1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiception/navbar-section-v1-light-74431b2140bbacd3.png
- Registry item: https://uiception.com/r/navbar-section-v1.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiception.com/r/navbar-section-v1.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://uiception.com/r/block-media.json`, `button`

## Files

### `registry/new-york/blocks/navbar-section-v1/page.tsx`

```tsx
import LoremDemo from "./components/navbar-lorem-demo"
import NavbarSectionV1 from "./components/navbar-section-v1"

export default function Page() {
  return (
    <>
      <NavbarSectionV1 />
      <LoremDemo />
    </>
  )
}
```

### `registry/new-york/blocks/navbar-section-v1/components/navbar-section-v1.tsx`

```tsx
import { NavbarSectionV1Root } from "./navbar-section-v1-root"
import { createBlockImage } from "@/lib/block-media"

const blockImage = createBlockImage("navbar-section-v1")
export default function NavbarSectionV1() {
  return <NavbarSectionV1Root logoSrc={blockImage("logo.svg")} />
}
```

### `registry/new-york/blocks/navbar-section-v1/components/navbar-section-v1-root.tsx`

```tsx
"use client"

import { useEffect, useRef, useState } from "react"
import Image from "next/image"
import Link from "next/link"

import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

function BrandLogo({
  className,
  src,
}: {
  className?: string
  src: string
}) {
  return (
    <Image
      alt=""
      aria-hidden
      className={cn("size-6 shrink-0", className)}
      height={32}
      src={src}
      unoptimized
      width={32}
    />
  )
}

function HamburgerIcon({ open }: { open: boolean }) {
  return (
    <div className="flex size-4 flex-col items-center justify-center gap-1">
      <span
        className={cn(
          "block h-px w-4 bg-current transition-all duration-300 ease-in-out",
          open ? "translate-y-[5px] rotate-45" : ""
        )}
      />
      <span
        className={cn(
          "block h-px w-4 bg-current transition-all duration-300 ease-in-out",
          open ? "scale-x-0 opacity-0" : ""
        )}
      />
      <span
        className={cn(
          "block h-px w-4 bg-current transition-all duration-300 ease-in-out",
          open ? "-translate-y-[5px] -rotate-45" : ""
        )}
      />
    </div>
  )
}

const NAV_LINKS = [
  { label: "How it works", href: "#", inPage: true },
  { label: "Source", href: "#", external: true },
  { label: "Sign in", href: "#", inPage: true },
]

const linkClass =
  "text-muted-foreground text-sm transition-colors hover:text-foreground"

export function NavbarSectionV1Root({ logoSrc }: { logoSrc: string }) {
  const [scrolled, setScrolled] = useState(false)
  const [menuOpen, setMenuOpen] = useState(false)
  const headerRef = useRef<HTMLElement>(null)

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 0)
    window.addEventListener("scroll", onScroll, { passive: true })
    return () => window.removeEventListener("scroll", onScroll)
  }, [])

  useEffect(() => {
    if (!menuOpen) return
    const onPointerDown = (e: PointerEvent) => {
      if (!headerRef.current?.contains(e.target as Node)) setMenuOpen(false)
    }
    document.addEventListener("pointerdown", onPointerDown)
    return () => document.removeEventListener("pointerdown", onPointerDown)
  }, [menuOpen])

  return (
    <header
      ref={headerRef}
      className={cn(
        "sticky top-0 z-40 border-b bg-background/80 backdrop-blur transition-colors duration-300",
        scrolled || menuOpen ? "border-border" : "border-transparent"
      )}
    >
      <div className="mx-auto flex h-16 w-full max-w-6xl items-center justify-between px-6">
        <Link
          aria-label="Homepage"
          className="inline-flex items-center gap-2 font-medium tracking-tight"
          href="#"
        >
          <BrandLogo src={logoSrc} />
          <span className="text-lg tracking-tight">uiception</span>
        </Link>

        <nav className="hidden items-center gap-6 md:flex">
          {NAV_LINKS.map(({ label, href, external, inPage }) => (
            <a
              key={label}
              className={linkClass}
              href={href}
              onClick={inPage ? (e) => e.preventDefault() : undefined}
              {...(external ? { target: "_blank", rel: "noreferrer" } : {})}
            >
              {label}
            </a>
          ))}
          <Button
            asChild
            className="h-8 gap-1 rounded-4xl px-3 active:not-aria-[haspopup]:translate-y-px"
          >
            <Link href="#">Get started</Link>
          </Button>
        </nav>

        <button
          aria-label={menuOpen ? "Close menu" : "Open menu"}
          className="text-muted-foreground transition-colors hover:text-foreground md:hidden"
          onClick={() => setMenuOpen((v) => !v)}
        >
          <HamburgerIcon open={menuOpen} />
        </button>
      </div>

      <div
        className={cn(
          "grid transition-all duration-300 ease-in-out md:hidden",
          menuOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
        )}
      >
        <div className="overflow-hidden">
          <div className="border-t border-border px-6 py-5">
            <nav className="flex flex-col gap-1">
              {NAV_LINKS.map(({ label, href, external, inPage }, i) => (
                <a
                  key={label}
                  className={cn(
                    linkClass,
                    "py-2 transition-all duration-300",
                    menuOpen
                      ? "translate-y-0 opacity-100"
                      : "-translate-y-2 opacity-0"
                  )}
                  href={href}
                  onClick={(e) => {
                    if (inPage) e.preventDefault()
                    setMenuOpen(false)
                  }}
                  style={{
                    transitionDelay: menuOpen ? `${i * 40 + 60}ms` : "0ms",
                  }}
                  {...(external ? { target: "_blank", rel: "noreferrer" } : {})}
                >
                  {label}
                </a>
              ))}
              <div
                className={cn(
                  "pt-2 transition-all duration-300",
                  menuOpen
                    ? "translate-y-0 opacity-100"
                    : "-translate-y-2 opacity-0"
                )}
                style={{
                  transitionDelay: menuOpen
                    ? `${NAV_LINKS.length * 40 + 60}ms`
                    : "0ms",
                }}
              >
                <Button
                  asChild
                  className="h-8 rounded-4xl px-3 active:not-aria-[haspopup]:translate-y-px"
                >
                  <Link href="#" onClick={() => setMenuOpen(false)}>
                    Get started
                  </Link>
                </Button>
              </div>
            </nav>
          </div>
        </div>
      </div>
    </header>
  )
}
```

### `registry/new-york/blocks/navbar-section-v1/components/navbar-lorem-demo.tsx`

```tsx
import { Instrument_Serif } from "next/font/google"

const instrumentSerif = Instrument_Serif({
  subsets: ["latin"],
  weight: ["400"],
})

function Spacer({ size = "md" }: { size?: "sm" | "md" | "lg" | "xl" }) {
  const heights = { sm: "h-4", md: "h-8", lg: "h-16", xl: "h-28" }
  return <div className={heights[size]} />
}

function Block({ rows = 1 }: { rows?: number }) {
  return (
    <div className="space-y-2">
      {Array.from({ length: rows }, (_, i) => (
        <div key={i} className="h-3" />
      ))}
    </div>
  )
}

export default function NavbarLoremDemo() {
  return (
    <main className="px-4 py-4 md:py-6 lg:py-8">
      <article
        aria-label="Sample document preview"
        className="mx-auto w-full max-w-2xl rounded-none border border-border bg-card text-card-foreground shadow-sm"
      >
        <header className="px-8 py-8 pt-10 sm:px-10">
          <span className={`${instrumentSerif.className} text-2xl text-foreground`}>
            uiception.com
          </span>
        </header>

        <div className="px-8 py-12 sm:px-10">
          <Spacer size="xl" />
          <Block rows={3} />
          <Spacer size="xl" />
          <Block rows={3} />
          <Spacer size="xl" />
          <div className="grid grid-cols-3 gap-6">
            {[0, 1, 2].map((i) => (
              <Block key={i} rows={3} />
            ))}
          </div>
          <Spacer size="xl" />
          <Block rows={3} />
          <Spacer size="xl" />
          <Block rows={3} />
          <Spacer size="xl" />
        </div>
      </article>
    </main>
  )
}
```

### `public/images/blocks/navbar-section-v1/logo.svg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/navbar-section-v1/logo.svg`

Left out to keep this page a readable size (780,247 characters). It is in the registry item above.
