Minimal Navbar

minimal

A clean, responsive navigation bar with smooth search expansion, shopping bag, and user icons. Perfect for portfolios and SaaS apps.

#minimal#responsive#clean#accessible

Preview

minimal-navbar
Interactive Preview
Click search or mobile icon to test responsive interactions.

Installation

Add this navbar component directly to your project using the shadcn CLI:

$npx shadcn@latest add https://navcn-phi.vercel.app/r/minimal-navbar.json

The component source code is added directly to your project so you can customize it freely.

Source Code

src/components/previews/minimal-navbar.tsx
tsx
"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { Menu, X, Search, ShoppingBag, User } from "lucide-react";
import Link from "next/link";

export function MinimalNavbar() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [showSearch, setShowSearch] = useState(false);

  const navLinks = [
    { label: "Home", href: "#" },
    { label: "About", href: "#" },
    { label: "Services", href: "#" },
    { label: "Contact", href: "#" },
  ];

  return (
    <motion.div
      initial={{ y: -20, opacity: 0 }}
      animate={{ y: 0, opacity: 1 }}
      transition={{ duration: 0.4, ease: "easeOut" }}
      className="h-auto p-2 rounded-3xl relative"
    >
      <motion.nav
        initial={{ y: -20, opacity: 0 }}
        animate={{ y: 0, opacity: 1 }}
        transition={{ duration: 0.4, ease: "easeOut" }}
      >
        <div className="max-w-7xl mx-auto px-4 lg:px-8">
          <div className="flex justify-between items-center h-16">
            {/* Logo */}
            <div className="flex-shrink-0 text-xl font-bold tracking-tight">
              <span className="text-foreground">Your Brand</span>
            </div>

            {/* Desktop Navigation */}
            <div className="hidden md:flex items-center space-x-8">
              {navLinks.map((link, i) => (
                <motion.div
                  key={link.label}
                  initial={{ opacity: 0, y: -5 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ delay: i * 0.05 }}
                >
                  <Link
                    href={link.href}
                    className="text-muted-foreground hover:text-foreground transition-colors font-medium text-sm"
                  >
                    {link.label}
                  </Link>
                </motion.div>
              ))}
            </div>

            {/* Desktop Actions */}
            <div className="hidden md:flex items-center space-x-3">
              {/* Search */}
              <div className="relative flex items-center">
                {!showSearch && (
                  <motion.button
                    key="search-icon"
                    initial={{ opacity: 0, scale: 0.8 }}
                    animate={{ opacity: 1, scale: 1 }}
                    exit={{ opacity: 0 }}
                    whileTap={{ scale: 0.9 }}
                    onClick={() => setShowSearch(true)}
                    className="p-2 rounded-full hover:bg-accent transition-colors"
                    aria-label="Open search"
                  >
                    <Search className="w-4 h-4" />
                  </motion.button>
                )}
                <AnimatePresence>
                  {showSearch && (
                    <motion.div
                      key="search-input"
                      initial={{ width: 40, opacity: 0 }}
                      animate={{ width: 200, opacity: 1 }}
                      exit={{ width: 40, opacity: 0 }}
                      transition={{ duration: 0.25, ease: "easeOut" }}
                      className="rounded-full bg-accent/40"
                    >
                      <div className="relative flex items-center">
                        <Search className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-4 h-4" />
                        <input
                          type="text"
                          placeholder="Search..."
                          value={searchQuery}
                          onChange={(e) => setSearchQuery(e.target.value)}
                          onBlur={() => setShowSearch(false)}
                          autoFocus
                          aria-label="Search"
                          className="pl-9 pr-4 py-2 w-full bg-transparent rounded-full text-sm outline-none"
                        />
                      </div>
                    </motion.div>
                  )}
                </AnimatePresence>
              </div>

              <motion.button
                whileTap={{ scale: 0.9 }}
                className="p-2 rounded-full hover:bg-accent transition-colors"
                aria-label="Shopping bag"
              >
                <ShoppingBag className="w-4 h-4" />
              </motion.button>
              <motion.button
                whileTap={{ scale: 0.9 }}
                className="p-2 rounded-full hover:bg-accent transition-colors"
                aria-label="User account"
              >
                <User className="w-4 h-4" />
              </motion.button>
            </div>

            {/* Mobile menu button */}
            <div className="md:hidden flex items-center space-x-2">
              <motion.button
                whileTap={{ scale: 0.9 }}
                className="p-2 rounded-full hover:bg-accent transition-colors"
                aria-label="Shopping bag"
              >
                <ShoppingBag className="w-5 h-5" />
              </motion.button>
              <motion.button
                whileTap={{ scale: 0.9 }}
                onClick={() => setIsMenuOpen(!isMenuOpen)}
                className="p-2 rounded-full hover:bg-accent transition-colors"
                aria-label={isMenuOpen ? "Close menu" : "Open menu"}
                aria-expanded={isMenuOpen}
              >
                {isMenuOpen ? (
                  <X className="w-5 h-5" />
                ) : (
                  <Menu className="w-5 h-5" />
                )}
              </motion.button>
            </div>
          </div>

          {/* Mobile Navigation */}
          <AnimatePresence>
            {isMenuOpen && (
              <motion.div
                initial={{ height: 0, opacity: 0 }}
                animate={{ height: "auto", opacity: 1 }}
                exit={{ height: 0, opacity: 0 }}
                transition={{ duration: 0.3, ease: "easeOut" }}
                className="md:hidden py-4 border-t border-border overflow-hidden"
              >
                <div className="flex flex-col space-y-3">
                  {navLinks.map((link, i) => (
                    <motion.div
                      key={link.label}
                      initial={{ opacity: 0, x: -10 }}
                      animate={{ opacity: 1, x: 0 }}
                      transition={{ delay: i * 0.05 }}
                    >
                      <Link
                        href={link.href}
                        className="block text-sm text-muted-foreground hover:text-foreground transition-colors py-1"
                      >
                        {link.label}
                      </Link>
                    </motion.div>
                  ))}

                  {/* Mobile Search */}
                  <motion.div
                    initial={{ opacity: 0, y: -5 }}
                    animate={{ opacity: 1, y: 0 }}
                    className="relative mt-2"
                  >
                    <Search className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-4 h-4" />
                    <input
                      type="text"
                      placeholder="Search..."
                      value={searchQuery}
                      onChange={(e) => setSearchQuery(e.target.value)}
                      aria-label="Search"
                      className="pl-9 pr-4 py-2 w-full rounded-full bg-accent/40 text-sm outline-none"
                    />
                  </motion.div>

                  <div className="flex items-center space-x-3 pt-2 border-t border-border">
                    <motion.button
                      whileTap={{ scale: 0.9 }}
                      className="p-2 rounded-full hover:bg-accent transition-colors"
                      aria-label="User account"
                    >
                      <User className="w-5 h-5" />
                    </motion.button>
                  </div>
                </div>
              </motion.div>
            )}
          </AnimatePresence>
        </div>
      </motion.nav>
    </motion.div>
  );
}

export default MinimalNavbar;

This is the exact source file installed into your project. You own and control the code.