# Navbar · HeroUI

> Migration guide for Navbar from HeroUI v2 to v3

- Page: https://jiguang132.com/systems/heroui/docs/migration/navbar
- Based on: HeroUI documentation (Apache-2.0) @ 60edd8ed751b1d2a270623fbb182bbcfa8baa9af

> 
  The Navbar component has been **removed** in HeroUI v3. Build navigation bars manually using native HTML elements and Tailwind CSS classes. This guide focuses on common patterns and simplifies complex features.


## Key Changes

### 1. Component Removal

**v2:** `` and subcomponents (`NavbarBrand`, `NavbarContent`, `NavbarItem`, `NavbarMenu`, `NavbarMenuItem`, `NavbarMenuToggle`)  
**v3:** Manual composition using native HTML elements

### 2. Subcomponents Mapping

| v2 Component | v3 Equivalent | Notes |
|-------------|---------------|-------|
| `Navbar` | `<nav>` element | Main container |
| `NavbarBrand` | `` or `<a>` | Logo/brand area |
| `NavbarContent` | `<ul>` element | Navigation list |
| `NavbarItem` | `<li>` element | Navigation item |
| `NavbarMenu` | Mobile menu overlay | Custom implementation |
| `NavbarMenuItem` | `<li>` in mobile menu | Mobile menu item |
| `NavbarMenuToggle` | `<button>` | Mobile menu toggle |

### 3. Features Removed

- `shouldHideOnScroll` - Requires custom scroll detection
- `isBlurred` - Use Tailwind `backdrop-blur` utilities
- `isBordered` - Use Tailwind border classes
- `position` variants - Use Tailwind `sticky`, `fixed` classes
- `maxWidth` variants - Use Tailwind `max-w-*` classes
- Mobile menu animations - Requires custom implementation
- Scroll blocking - Requires custom implementation (see "Scroll Blocking" below)

## Migration Examples

### Basic Navbar


  

#### v2


    ```tsx
    import { Navbar, NavbarBrand, NavbarContent, NavbarItem, Link, Button } from "@heroui/react";

    {/* Basic */}
    <Navbar>
      <NavbarBrand>
        <Logo />
        <p className="font-bold">ACME</p>
      </NavbarBrand>
      <NavbarContent>
        <NavbarItem><Link href="#">Features</Link></NavbarItem>
        <NavbarItem><Link href="#">Pricing</Link></NavbarItem>
      </NavbarContent>
    </Navbar>
    
    {/* With right-aligned content */}
    <Navbar>
      <NavbarBrand>Logo</NavbarBrand>
      <NavbarContent justify="end">
        <NavbarItem><Button>Sign Up</Button></NavbarItem>
      </NavbarContent>
    </Navbar>
    ```
  
  

#### v3


    ```tsx
    import { Link, Button } from "@heroui/react";

    {/* Basic */}
    <nav className="sticky top-0 z-40 w-full border-b border-separator bg-background/70 backdrop-blur-lg">
      <header className="flex h-16 items-center justify-between px-6">
        <div className="flex items-center gap-3">
          <Logo />
          <p className="font-bold">ACME</p>
        </div>
        <ul className="flex items-center gap-4">
          <li><Link href="#">Features</Link></li>
          <li><Link href="#">Pricing</Link></li>
        </ul>
      </header>
    </nav>
    
    {/* With right-aligned content */}
    <nav className="sticky top-0 z-40 w-full border-b border-separator bg-background/70 backdrop-blur-lg">
      <header className="flex h-16 items-center justify-between px-6">
        <div>Logo</div>
        <ul className="flex items-center gap-4">
          <li><Button>Sign Up</Button></li>
        </ul>
      </header>
    </nav>
    ```
  


### With Mobile Menu (Simplified)


  

#### v2


    ```tsx
    import {
      Navbar,
      NavbarBrand,
      NavbarContent,
      NavbarItem,
      NavbarMenu,
      NavbarMenuItem,
      NavbarMenuToggle,
    } from "@heroui/react";

    function App() {
      const [isMenuOpen, setIsMenuOpen] = useState(false);

      return (
        <Navbar onMenuOpenChange={setIsMenuOpen}>
          <NavbarContent>
            <NavbarMenuToggle className="sm:hidden" />
            <NavbarBrand>Logo</NavbarBrand>
          </NavbarContent>
          <NavbarContent className="hidden md:flex">
            <NavbarItem>Features</NavbarItem>
            <NavbarItem>Pricing</NavbarItem>
          </NavbarContent>
          <NavbarMenu>
            <NavbarMenuItem>Features</NavbarMenuItem>
            <NavbarMenuItem>Pricing</NavbarMenuItem>
          </NavbarMenu>
        </Navbar>
      );
    }
    ```
  
  

#### v3


    ```tsx
    import { useState } from "react";
    import { Link, Button } from "@heroui/react";

    function App() {
      const [isMenuOpen, setIsMenuOpen] = useState(false);

      return (
        <nav className="sticky top-0 z-40 w-full border-b border-separator bg-background/70 backdrop-blur-lg">
          <header className="flex h-16 items-center justify-between px-6">
            <div className="flex items-center gap-4">
              <button
                className="md:hidden"
                onClick={() => setIsMenuOpen(!isMenuOpen)}
                aria-label="Toggle menu"
              >
                <span className="sr-only">Menu</span>
                <svg
                  className="h-6 w-6"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  {isMenuOpen ? (
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M6 18L18 6M6 6l12 12"
                    />
                  ) : (
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M4 6h16M4 12h16M4 18h16"
                    />
                  )}
                </svg>
              </button>
              <div>Logo</div>
            </div>
            <ul className="hidden items-center gap-4 md:flex">
              <li>
                <Link href="#">Features</Link>
              </li>
              <li>
                <Link href="#">Pricing</Link>
              </li>
            </ul>
          </header>
          {isMenuOpen && (
            <div className="border-t border-separator md:hidden">
              <ul className="flex flex-col gap-2 p-4">
                <li>
                  <Link href="#" className="block py-2">
                    Features
                  </Link>
                </li>
                <li>
                  <Link href="#" className="block py-2">
                    Pricing
                  </Link>
                </li>
              </ul>
            </div>
          )}
        </nav>
      );
    }
    ```
  


## Complete Example


  

#### v2


    ```tsx
    import {
      Navbar,
      NavbarBrand,
      NavbarContent,
      NavbarItem,
      NavbarMenu,
      NavbarMenuItem,
      NavbarMenuToggle,
      Link,
      Button,
    } from "@heroui/react";

    export default function App() {
      const [isMenuOpen, setIsMenuOpen] = useState(false);

      return (
        <Navbar position="sticky" maxWidth="lg" onMenuOpenChange={setIsMenuOpen}>
          <NavbarContent>
            <NavbarMenuToggle className="sm:hidden" />
            <NavbarBrand>
              <Logo />
              <p className="font-bold">ACME</p>
            </NavbarBrand>
          </NavbarContent>
          <NavbarContent className="hidden md:flex">
            <NavbarItem>
              <Link href="#">Features</Link>
            </NavbarItem>
            <NavbarItem isActive>
              <Link href="#">Dashboard</Link>
            </NavbarItem>
            <NavbarItem>
              <Link href="#">Pricing</Link>
            </NavbarItem>
          </NavbarContent>
          <NavbarContent justify="end">
            <NavbarItem>
              <Link href="#">Login</Link>
            </NavbarItem>
            <NavbarItem>
              <Button>Sign Up</Button>
            </NavbarItem>
          </NavbarContent>
          <NavbarMenu>
            <NavbarMenuItem>
              <Link href="#">Features</Link>
            </NavbarMenuItem>
            <NavbarMenuItem>
              <Link href="#">Dashboard</Link>
            </NavbarMenuItem>
            <NavbarMenuItem>
              <Link href="#">Pricing</Link>
            </NavbarMenuItem>
          </NavbarMenu>
        </Navbar>
      );
    }
    ```
  
  

#### v3


    ```tsx
    import { useState } from "react";
    import { Link, Button } from "@heroui/react";

    export default function App() {
      const [isMenuOpen, setIsMenuOpen] = useState(false);

      return (
        <nav className="sticky top-0 z-40 w-full border-b border-separator bg-background/70 backdrop-blur-lg">
          <header className="mx-auto flex h-16 max-w-5xl items-center justify-between px-6">
            <div className="flex items-center gap-4">
              <button
                className="md:hidden"
                onClick={() => setIsMenuOpen(!isMenuOpen)}
                aria-label="Toggle menu"
                aria-expanded={isMenuOpen}
              >
                <span className="sr-only">Menu</span>
                <svg
                  className="h-6 w-6"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  {isMenuOpen ? (
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M6 18L18 6M6 6l12 12"
                    />
                  ) : (
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth={2}
                      d="M4 6h16M4 12h16M4 18h16"
                    />
                  )}
                </svg>
              </button>
              <div className="flex items-center gap-3">
                <Logo />
                <p className="font-bold">ACME</p>
              </div>
            </div>
            <ul className="hidden items-center gap-4 md:flex">
              <li>
                <Link href="#">Features</Link>
              </li>
              <li>
                <Link href="#" className="font-medium text-accent" aria-current="page">
                  Dashboard
                </Link>
              </li>
              <li>
                <Link href="#">Pricing</Link>
              </li>
            </ul>
            <div className="hidden items-center gap-4 md:flex">
              <Link href="#">Login</Link>
              <Button>Sign Up</Button>
            </div>
          </header>
          {isMenuOpen && (
            <div className="border-t border-separator md:hidden">
              <ul className="flex flex-col gap-2 p-4">
                <li>
                  <Link href="#" className="block py-2">
                    Features
                  </Link>
                </li>
                <li>
                  <Link href="#" className="block py-2 font-medium text-accent">
                    Dashboard
                  </Link>
                </li>
                <li>
                  <Link href="#" className="block py-2">
                    Pricing
                  </Link>
                </li>
                <li className="mt-4 flex flex-col gap-2 border-t border-separator pt-4">
                  <Link href="#" className="block py-2">
                    Login
                  </Link>
                  <Button className="w-full">Sign Up</Button>
                </li>
              </ul>
            </div>
          )}
        </nav>
      );
    }
    ```
  


## Creating a Reusable Navbar Component (Recommended)

Since navbars are commonly needed, here's a simplified reusable component:

```tsx
import { useState, ReactNode } from "react";
import { Link, Button } from "@heroui/react";
import { cn } from "@/lib/utils"; // or your cn utility

interface NavbarItem {
  label: string;
  href: string;
  isActive?: boolean;
}

interface NavbarProps {
  brand: ReactNode;
  items: NavbarItem[];
  rightContent?: ReactNode;
  className?: string;
  maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
  position?: "static" | "sticky" | "fixed";
}

const maxWidthClasses = {
  sm: "max-w-[640px]",
  md: "max-w-[768px]",
  lg: "max-w-[1024px]",
  xl: "max-w-[1280px]",
  "2xl": "max-w-[1536px]",
  full: "max-w-full",
};

export function Navbar({
  brand,
  items,
  rightContent,
  className,
  maxWidth = "lg",
  position = "sticky",
}: NavbarProps) {
  const [isMenuOpen, setIsMenuOpen] = useState(false);

  return (
    <nav
      className={cn(
        "z-40 w-full border-b border-separator bg-background/70 backdrop-blur-lg",
        position === "sticky" && "sticky top-0",
        position === "fixed" && "fixed top-0",
        className
      )}
    >
      <header
        className={cn(
          "flex h-16 items-center justify-between px-6",
          maxWidth !== "full" && maxWidthClasses[maxWidth],
          "mx-auto"
        )}
      >
        <div className="flex items-center gap-4">
          <button
            className="md:hidden"
            onClick={() => setIsMenuOpen(!isMenuOpen)}
            aria-label="Toggle menu"
            aria-expanded={isMenuOpen}
          >
            <span className="sr-only">Menu</span>
            <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              {isMenuOpen ? (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M6 18L18 6M6 6l12 12"
                />
              ) : (
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M4 6h16M4 12h16M4 18h16"
                />
              )}
            </svg>
          </button>
          {brand}
        </div>
        <ul className="hidden items-center gap-4 md:flex">
          {items.map((item) => (
            <li key={item.href}>
              <Link
                href={item.href}
                className={cn(item.isActive && "font-medium text-accent")}
                aria-current={item.isActive ? "page" : undefined}
              >
                {item.label}
              </Link>
            </li>
          ))}
        </ul>
        {rightContent && <div className="hidden items-center gap-4 md:flex">{rightContent}</div>}
      </header>
      {isMenuOpen && (
        <div className="border-t border-separator md:hidden">
          <ul className="flex flex-col gap-2 p-4">
            {items.map((item) => (
              <li key={item.href}>
                <Link
                  href={item.href}
                  className={cn(
                    "block py-2",
                    item.isActive && "font-medium text-accent"
                  )}
                >
                  {item.label}
                </Link>
              </li>
            ))}
            {rightContent && (
              <li className="mt-4 flex flex-col gap-2 border-t border-separator pt-4">
                {rightContent}
              </li>
            )}
          </ul>
        </div>
      )}
    </nav>
  );
}

// Usage
<Navbar
  brand={
    <>
      <Logo />
      <p className="font-bold">ACME</p>
    </>
  }
  items={[
    { label: "Features", href: "#features" },
    { label: "Dashboard", href: "#dashboard", isActive: true },
    { label: "Pricing", href: "#pricing" },
  ]}
  rightContent={
    <>
      <Link href="#login">Login</Link>
      <Button>Sign Up</Button>
    </>
  }
/>
```

## Advanced Features (Custom Implementation Required)

### Hide on Scroll

The `shouldHideOnScroll` feature requires custom scroll detection:

```tsx
import { useState, useEffect } from "react";

function useScrollDirection() {
  const [isHidden, setIsHidden] = useState(false);
  const [lastScrollY, setLastScrollY] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      const currentScrollY = window.scrollY;
      setIsHidden(currentScrollY > lastScrollY && currentScrollY > 64);
      setLastScrollY(currentScrollY);
    };

    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, [lastScrollY]);

  return isHidden;
}

function NavbarWithHideOnScroll() {
  const isHidden = useScrollDirection();

  return (
    <nav
      className={cn(
        "sticky top-0 z-40 w-full transition-transform duration-300",
        isHidden && "-translate-y-full"
      )}
    >
      {/* navbar content */}
    </nav>
  );
}
```

### Scroll Blocking

For blocking scroll when mobile menu is open:

```tsx
useEffect(() => {
  if (isMenuOpen) {
    document.body.style.overflow = "hidden";
  } else {
    document.body.style.overflow = "";
  }
  return () => {
    document.body.style.overflow = "";
  };
}, [isMenuOpen]);
```

## Summary

1. **Component Removed**: All Navbar components removed (`Navbar`, `NavbarBrand`, `NavbarContent`, `NavbarItem`, `NavbarMenu`, `NavbarMenuItem`, `NavbarMenuToggle`)
2. **Import Change**: Remove all Navbar imports from `@heroui/react`
3. **Manual Composition**: Build navbars using native HTML elements
4. **Mobile Menu**: Implement mobile menu manually with state management
5. **Styling**: Apply Tailwind CSS classes directly
6. **Advanced Features**: Hide on scroll, animations require custom implementation

## Migration Steps

1. **Remove Imports**: Remove all Navbar-related imports
2. **Replace Structure**: Replace `` with `<nav>` element
3. **Replace Subcomponents**: Replace subcomponents with semantic HTML (`<header>`, `<ul>`, `<li>`)
4. **Add Mobile Menu**: Implement mobile menu toggle and menu manually
5. **Apply Styling**: Use Tailwind CSS classes for layout and styling
6. **Handle State**: Manage mobile menu state with React `useState`
7. **Optional**: Create reusable Navbar component for your application

## Common Patterns

### Simple Navigation

```tsx
<nav className="sticky top-0 z-40 w-full border-b border-separator bg-background">
  <header className="flex h-16 items-center justify-between px-6">
    <div>Logo</div>
    <ul className="flex items-center gap-4">
      <li><Link href="#">Home</Link></li>
      <li><Link href="#">About</Link></li>
      <li><Link href="#">Contact</Link></li>
    </ul>
  </header>
</nav>
```

### With Dropdown (using v3 Dropdown)

```tsx
import { Dropdown, Button, Label } from "@heroui/react";

<ul className="flex items-center gap-4">
  <li>
    <Dropdown>
      <Button variant="ghost">Features</Button>
      <Dropdown.Popover>
        <Dropdown.Menu>
          <Dropdown.Item id="feature1" textValue="Feature 1">
            <Label>Feature 1</Label>
          </Dropdown.Item>
          <Dropdown.Item id="feature2" textValue="Feature 2">
            <Label>Feature 2</Label>
          </Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown.Popover>
    </Dropdown>
  </li>
</ul>
```

---

Index of all JGuang resources: https://jiguang132.com/llms.txt
