Morph Navbar
atlas-ui/morph-navbarFreeComponent
A polished navigation bar with a fluid, morphing pill that tracks the active link and hover state.
Live preview
live · rendered by the registry
Rendered by atlas-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://atlasui.dev/r/morph-navbar.jsonSource
1"use client";23import * as React from "react";45import Link from "next/link";67import { motion } from "motion/react";89import { cn } from "@/lib/utils";1011interface NavbarContextValue {12 registerItem: (href: string, el: HTMLDivElement | null) => void;13 previewItem: (el: HTMLDivElement) => void;14 clearPreview: () => void;15 activeHref: string;16}1718export const MorphNavbarContext =19 React.createContext<NavbarContextValue | null>(null);2021export function MorphNavbar({22 children,23 activeHref,24}: {25 children: React.ReactNode;26 activeHref: string;27}) {28 const containerRef = React.useRef<HTMLDivElement>(null);2930 // Registry of nav items by href31 const itemRegistry = React.useRef<Record<string, HTMLDivElement | null>>({});3233 const lastActivePosition = React.useRef<{34 left: string;35 width: string;36 } | null>(null);3738 const [activePill, setActivePill] = React.useState({39 left: "4px",40 width: "0px",41 });4243 const [hoverPill, setHoverPill] = React.useState({44 left: "4px",45 width: "calc(100% - 8px)",46 });4748 function measureItem(el: HTMLDivElement) {49 if (!containerRef.current) return null;5051 const itemRect = el.getBoundingClientRect();52 const containerRect = containerRef.current.getBoundingClientRect();5354 return {55 left: `${itemRect.left - containerRect.left}px`,56 width: `${itemRect.width}px`,57 };58 }5960 function updateActivePill(next: { left: string; width: string }) {61 // Avoid unnecessary state updates (prevents render loops)62 if (63 lastActivePosition.current &&64 lastActivePosition.current.left === next.left &&65 lastActivePosition.current.width === next.width66 ) {67 return;68 }6970 lastActivePosition.current = next;71 setActivePill(next);72 }7374 const contextValue: NavbarContextValue = {75 registerItem(href, el) {76 itemRegistry.current[href] = el;7778 // Only position the active pill for the active route79 if (!el || href !== activeHref) return;8081 const pos = measureItem(el);82 if (pos) updateActivePill(pos);83 },8485 previewItem(el) {86 const pos = measureItem(el);87 if (pos) setHoverPill(pos);88 },8990 clearPreview() {91 setHoverPill({92 left: "4px",93 width: "calc(100% - 8px)",94 });95 },9697 activeHref,98 };99100 return (101 <MorphNavbarContext.Provider value={contextValue}>102 <nav103 aria-label="Main Navigation"104// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from atlas-ui
All 99 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Mobile Navbaranimated-mobile-navbar | atlas-ui | Components | Free | 2 deps | |
| Blend Mouse Trailerblend-mouse-trailer | atlas-ui | Components | Free | 1 dep | |
| Blob Mouse Trailerblob-mouse-trailer | atlas-ui | Components | Free | 1 dep | |
| Box Revealbox-reveal | atlas-ui | Components | Free | 1 dep | |
| Cascade Textcascade-text | atlas-ui | Components | Free | 1 dep | |
| Dot And Circle Mouse Trailerdot-and-circle-mouse-trailer | atlas-ui | Components | Free | 1 dep | |
| Fancy Theme Togglefancy-theme-toggle | atlas-ui | Components | Free | 1 dep | |
| Hacker Texthacker-text | atlas-ui | Components | Free | no deps |
