Logo

Voxlet UI

Anime Navbar

A sleek and animated navigation bar built with Framer Motion for a smooth user experience.

The Navbar will show at top of the page

Installation

  1. 1

    Install Dependencies

    npm i framer-motion lucide-react
  2. 2

    lib/utils.ts

    import { clsx, type ClassValue } from "clsx"
    import { twMerge } from "tailwind-merge"
        
    export function cn(...inputs: ClassValue[]) {
      return twMerge(clsx(inputs))
    }
  3. 3

    Copy the source code

    @/components/ui/anime-navbar.tsx
    "use client"
    
    import React, { useEffect, useState } from "react"
    import { motion, AnimatePresence } from "framer-motion"
    import Link from "next/link"
    import { usePathname } from "next/navigation"
    import { LucideIcon } from "lucide-react"
    import { cn } from "@/lib/utils"
    
    interface NavItem {
        name: string
        url: string
        icon: LucideIcon
    }
    
    interface NavBarProps {
        items: NavItem[]
        className?: string
        defaultActive?: string
    }
    
    export function AnimeNavBar({ items, className, defaultActive = "Home" }: NavBarProps) {
        const pathname = usePathname()
        const [mounted, setMounted] = useState(false)
        const [hoveredTab, setHoveredTab] = useState<string | null>(null)
        const [activeTab, setActiveTab] = useState<string>(defaultActive)
        const [isMobile, setIsMobile] = useState(false)
    
        useEffect(() => {
            setMounted(true)
        }, [])
    
        useEffect(() => {
            const handleResize = () => {
                setIsMobile(window.innerWidth < 768)
            }
    
            handleResize()
            window.addEventListener("resize", handleResize)
            return () => window.removeEventListener("resize", handleResize)
        }, [])
    
        if (!mounted) return null
    
        return (
            <div className="fixed top-5 left-0 right-0 z-[9999]">
                <div className="flex justify-center pt-6">
                    <motion.div
                        className="flex items-center gap-3 bg-black/50 border border-white/10 backdrop-blur-lg py-2 px-2 rounded-full shadow-lg relative"
                        initial={{ y: -20, opacity: 0 }}
                        animate={{ y: 0, opacity: 1 }}
                        transition={{
                            type: "spring",
                            stiffness: 260,
                            damping: 20,
                        }}
                    >
                        {items.map((item) => {
                            const Icon = item.icon
                            const isActive = activeTab === item.name
                            const isHovered = hoveredTab === item.name
    
                            return (
                                <Link
                                    key={item.name}
                                    href={item.url}
                                    onClick={(e) => {
                                        e.preventDefault()
                                        setActiveTab(item.name)
                                    }}
                                    onMouseEnter={() => setHoveredTab(item.name)}
                                    onMouseLeave={() => setHoveredTab(null)}
                                    className={cn(
                                        "relative cursor-pointer text-sm font-semibold px-6 py-3 rounded-full transition-all duration-300",
                                        "text-white/70 hover:text-white",
                                        isActive && "text-white"
                                    )}
                                >
                                    {isActive && (
                                        <motion.div
                                            className="absolute inset-0 rounded-full -z-10 overflow-hidden"
                                            initial={{ opacity: 0 }}
                                            animate={{
                                                opacity: [0.3, 0.5, 0.3],
                                                scale: [1, 1.03, 1]
                                            }}
                                            transition={{
                                                duration: 2,
                                                repeat: Infinity,
                                                ease: "easeInOut"
                                            }}
                                        >
                                            <div className="absolute inset-0 bg-primary/25 rounded-full blur-md" />
                                            <div className="absolute inset-[-4px] bg-primary/20 rounded-full blur-xl" />
                                            <div className="absolute inset-[-8px] bg-primary/15 rounded-full blur-2xl" />
                                            <div className="absolute inset-[-12px] bg-primary/5 rounded-full blur-3xl" />
    
                                            <div
                                                className="absolute inset-0 bg-gradient-to-r from-primary/0 via-primary/20 to-primary/0"
                                                style={{
                                                    animation: "shine 3s ease-in-out infinite"
                                                }}
                                            />
                                        </motion.div>
                                    )}
    
                                    <motion.span
                                        className="hidden md:inline relative z-10"
                                        initial={{ opacity: 0 }}
                                        animate={{ opacity: 1 }}
                                        transition={{ duration: 0.2 }}
                                    >
                                        {item.name}
                                    </motion.span>
                                    <motion.span
                                        className="md:hidden relative z-10"
                                        whileHover={{ scale: 1.2 }}
                                        whileTap={{ scale: 0.9 }}
                                    >
                                        <Icon size={18} strokeWidth={2.5} />
                                    </motion.span>
    
                                    <AnimatePresence>
                                        {isHovered && !isActive && (
                                            <motion.div
                                                initial={{ opacity: 0, scale: 0.8 }}
                                                animate={{ opacity: 1, scale: 1 }}
                                                exit={{ opacity: 0, scale: 0.8 }}
                                                className="absolute inset-0 bg-white/10 rounded-full -z-10"
                                            />
                                        )}
                                    </AnimatePresence>
    
                                    {isActive && (
                                        <motion.div
                                            layoutId="anime-mascot"
                                            className="absolute -top-12 left-1/2 -translate-x-1/2 pointer-events-none"
                                            initial={false}
                                            transition={{
                                                type: "spring",
                                                stiffness: 300,
                                                damping: 30,
                                            }}
                                        >
                                            <div className="relative w-12 h-12">
                                                <motion.div
                                                    className="absolute w-10 h-10 bg-white rounded-full left-1/2 -translate-x-1/2"
                                                    animate={
                                                        hoveredTab ? {
                                                            scale: [1, 1.1, 1],
                                                            rotate: [0, -5, 5, 0],
                                                            transition: {
                                                                duration: 0.5,
                                                                ease: "easeInOut"
                                                            }
                                                        } : {
                                                            y: [0, -3, 0],
                                                            transition: {
                                                                duration: 2,
                                                                repeat: Infinity,
                                                                ease: "easeInOut"
                                                            }
                                                        }
                                                    }
                                                >
                                                    <motion.div
                                                        className="absolute w-2 h-2 bg-black rounded-full"
                                                        animate={
                                                            hoveredTab ? {
                                                                scaleY: [1, 0.2, 1],
                                                                transition: {
                                                                    duration: 0.2,
                                                                    times: [0, 0.5, 1]
                                                                }
                                                            } : {}
                                                        }
                                                        style={{ left: '25%', top: '40%' }}
                                                    />
                                                    <motion.div
                                                        className="absolute w-2 h-2 bg-black rounded-full"
                                                        animate={
                                                            hoveredTab ? {
                                                                scaleY: [1, 0.2, 1],
                                                                transition: {
                                                                    duration: 0.2,
                                                                    times: [0, 0.5, 1]
                                                                }
                                                            } : {}
                                                        }
                                                        style={{ right: '25%', top: '40%' }}
                                                    />
                                                    <motion.div
                                                        className="absolute w-2 h-1.5 bg-pink-300 rounded-full"
                                                        animate={{
                                                            opacity: hoveredTab ? 0.8 : 0.6
                                                        }}
                                                        style={{ left: '15%', top: '55%' }}
                                                    />
                                                    <motion.div
                                                        className="absolute w-2 h-1.5 bg-pink-300 rounded-full"
                                                        animate={{
                                                            opacity: hoveredTab ? 0.8 : 0.6
                                                        }}
                                                        style={{ right: '15%', top: '55%' }}
                                                    />
    
                                                    <motion.div
                                                        className="absolute w-4 h-2 border-b-2 border-black rounded-full"
                                                        animate={
                                                            hoveredTab ? {
                                                                scaleY: 1.5,
                                                                y: -1
                                                            } : {
                                                                scaleY: 1,
                                                                y: 0
                                                            }
                                                        }
                                                        style={{ left: '30%', top: '60%' }}
                                                    />
                                                    <AnimatePresence>
                                                        {hoveredTab && (
                                                            <>
                                                                <motion.div
                                                                    initial={{ opacity: 0, scale: 0 }}
                                                                    animate={{ opacity: 1, scale: 1 }}
                                                                    exit={{ opacity: 0, scale: 0 }}
                                                                    className="absolute -top-1 -right-1 w-2 h-2 text-yellow-300"
                                                                >
    
                                                                </motion.div>
                                                                <motion.div
                                                                    initial={{ opacity: 0, scale: 0 }}
                                                                    animate={{ opacity: 1, scale: 1 }}
                                                                    exit={{ opacity: 0, scale: 0 }}
                                                                    transition={{ delay: 0.1 }}
                                                                    className="absolute -top-2 left-0 w-2 h-2 text-yellow-300"
                                                                >
    
                                                                </motion.div>
                                                            </>
                                                        )}
                                                    </AnimatePresence>
                                                </motion.div>
                                                <motion.div
                                                    className="absolute -bottom-1 left-1/2 w-4 h-4 -translate-x-1/2"
                                                    animate={
                                                        hoveredTab ? {
                                                            y: [0, -4, 0],
                                                            transition: {
                                                                duration: 0.3,
                                                                repeat: Infinity,
                                                                repeatType: "reverse"
                                                            }
                                                        } : {
                                                            y: [0, 2, 0],
                                                            transition: {
                                                                duration: 1,
                                                                repeat: Infinity,
                                                                ease: "easeInOut",
                                                                delay: 0.5
                                                            }
                                                        }
                                                    }
                                                >
                                                    <div className="w-full h-full bg-white rotate-45 transform origin-center" />
                                                </motion.div>
                                            </div>
                                        </motion.div>
                                    )}
                                </Link>
                            )
                        })}
                    </motion.div>
                </div>
            </div>
        )
    } 

Props

NavBarProps

PropTypeDefaultDescription
itemsNavItem[][]An array of navigation items, each containing a name, URL, and icon.
classNamestringundefinedOptional custom class names for styling.
defaultActivestringundefinedSpecifies which navigation item should be active by default.

NavItem

PropTypeDefaultDescription
namestringundefinedThe label for the navigation item.
urlstringundefinedThe destination URL for the navigation item.
iconLucideIconundefinedThe icon representing the navigation item.

Inspired by Jatin Yadav's Anime Navbar component on 21st.dev.

Brought to you by Voxlet