# Floating Navbar

> Floating pill navbar that hides on scroll down and returns on scroll up or focus.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/floating-navbar
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/floating-navbar.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

No extra dependencies.

## Usage

```tsx title="components/floating-navbar-demo.tsx"
"use client"

import * as React from "react"

import { FloatingNavbar } from "@/components/floating-navbar"

const links = [
  { label: "Home", href: "#home" },
  { label: "Work", href: "#work" },
  { label: "About", href: "#about" },
  { label: "Contact", href: "#contact" },
]

export default function FloatingNavbarDemo() {
  const scroller = React.useRef<HTMLDivElement>(null)

  return (
    <div className="relative w-full max-w-2xl overflow-hidden rounded-2xl border bg-card">
      <div ref={scroller} className="h-80 overflow-y-auto pt-4">
        <FloatingNavbar
          links={links}
          logo="Atelier"
          scrollTarget={scroller}
          action={
            <a href="#hire" className="inline-flex rounded-full bg-primary px-3.5 py-1.5 text-sm font-medium text-primary-foreground">
              Hire me
            </a>
          }
        />
        <p className="px-6 pt-4 text-center text-xs text-muted-foreground">Scroll down to hide the navbar, scroll up to bring it back.</p>
        <div className="grid gap-4 p-6">
          {Array.from({ length: 8 }, (_, index) => (
            <div key={index} className="h-28 rounded-xl bg-gradient-to-br from-muted to-muted/40" />
          ))}
        </div>
      </div>
    </div>
  )
}
```

## Source

```tsx title="components/floating-navbar.tsx"
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"

export type FloatingNavLink = { label: string; href: string }

export interface FloatingNavbarProps extends Omit<React.ComponentProps<"header">, "children"> {
  links: FloatingNavLink[]
  logo?: React.ReactNode
  action?: React.ReactNode
  /** Scroll container to watch; the window when omitted */
  scrollTarget?: React.RefObject<HTMLElement | null>
  defaultActive?: string
}

// Pill navbar that slides away while scrolling down and returns on scroll up or keyboard focus.
export function FloatingNavbar({ links, logo, action, scrollTarget, defaultActive, className, ...props }: FloatingNavbarProps) {
  const [hidden, setHidden] = React.useState(false)
  const [active, setActive] = React.useState(defaultActive ?? links[0]?.href)
  const last = React.useRef(0)

  React.useEffect(() => {
    const element = scrollTarget?.current
    const source: HTMLElement | Window = element ?? window
    const read = () => (element ? element.scrollTop : window.scrollY)
    const onScroll = () => {
      const y = read()
      setHidden(y > last.current && y > 80)
      last.current = y
    }
    source.addEventListener("scroll", onScroll, { passive: true })
    return () => source.removeEventListener("scroll", onScroll)
  }, [scrollTarget])

  return (
    <header
      onFocus={() => setHidden(false)}
      className={cn(
        "sticky top-4 z-30 flex justify-center px-4 transition-[transform,opacity] duration-300 ease-out",
        hidden && "pointer-events-none -translate-y-[160%] opacity-0",
        className
      )}
      {...props}
    >
      <nav
        aria-label="Main"
        className="flex items-center gap-1 rounded-full border bg-background/80 p-1.5 text-foreground shadow-lg backdrop-blur-md supports-[backdrop-filter]:bg-background/60"
      >
        {logo && <span className="px-3 text-sm font-semibold">{logo}</span>}
        {links.map((link) => (
          <a
            key={link.href}
            href={link.href}
            aria-current={active === link.href ? "page" : undefined}
            onClick={() => setActive(link.href)}
            className="rounded-full px-3.5 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground aria-[current=page]:bg-foreground aria-[current=page]:text-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
          >
            {link.label}
          </a>
        ))}
        {action && <span className="pl-1">{action}</span>}
      </nav>
    </header>
  )
}
```
