FluxUI Pro is live - modern UI, powerful animations, zero hassle.
Components
Navigation Menu

Navigation Menu

Animated navigation menu with nested submenus and smooth transitions.

Installation

1

Install the packages

npm i motion clsx tailwind-merge
2

Add util file

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

Copy and paste the following code into your project

navigation-menu.tsx
"use client"; import { cn } from "@/lib/utils"; import { motion, AnimatePresence } from "motion/react"; import React, { useState } from "react"; import { FiChevronDown, FiChevronRight } from "react-icons/fi"; interface MenuItem { label: string; href?: string; children?: MenuItem[]; icon?: React.ReactNode; } interface NavigationMenuProps { items?: MenuItem[]; orientation?: "horizontal" | "vertical"; } const NavigationMenu: React.FC<NavigationMenuProps> = ({ items = [ { label: "Home", href: "/", icon: <span>🏠</span> }, { label: "Components", children: [ { label: "Buttons", href: "/components/buttons" }, { label: "Forms", href: "/components/forms" }, { label: "Layout", children: [ { label: "Grid", href: "/components/grid" }, { label: "Flex", href: "/components/flex" } ] } ] }, { label: "Documentation", href: "/docs" } ], orientation = "horizontal" }) => { const [openMenus, setOpenMenus] = useState<Set<string>>(new Set()); const toggleMenu = (label: string) => { const newOpenMenus = new Set(openMenus); if (newOpenMenus.has(label)) { newOpenMenus.delete(label); } else { newOpenMenus.add(label); } setOpenMenus(newOpenMenus); }; const renderMenuItem = (item: MenuItem, depth = 0): React.ReactNode => { const hasChildren = item.children && item.children.length > 0; const isOpen = openMenus.has(item.label); return ( <div key={item.label} className="relative"> <motion.div className={cn( "flex items-center gap-2 px-3 py-2 rounded-lg cursor-pointer", "hover:bg-neutral-700 transition-colors", orientation === "vertical" && "w-full justify-between", depth > 0 && "ml-4" )} onClick={() => hasChildren && toggleMenu(item.label)} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} > {item.icon && <span className="text-neutral-400">{item.icon}</span>} <span className="text-neutral-300">{item.label}</span> {hasChildren && ( <motion.div animate={{ rotate: isOpen ? 90 : 0 }} transition={{ duration: 0.2 }} > <FiChevronRight className="h-4 w-4 text-neutral-400" /> </motion.div> )} </motion.div> <AnimatePresence> {hasChildren && isOpen && ( <motion.div className={cn( "mt-1", orientation === "horizontal" && "absolute top-full left-0 z-10", orientation === "vertical" && "ml-4" )} initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: "auto" }} exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.2 }} > <div className={cn( "bg-neutral-800 rounded-lg border border-neutral-700 shadow-lg", orientation === "horizontal" && "min-w-48 p-2", orientation === "vertical" && "border-l-4 border-l-cyan-500" )}> {item.children?.map((child) => renderMenuItem(child, depth + 1))} </div> </motion.div> )} </AnimatePresence> </div> ); }; return ( <nav className={cn( "flex gap-2", orientation === "vertical" && "flex-col" )}> {items.map((item) => renderMenuItem(item))} </nav> ); }; export default NavigationMenu;
4

Update the import paths to match your project setup

Props

PropTypeDefaultDescription
itemsMenuItem[][]Array of menu items with nested children support.
orientationstringhorizontalMenu orientation (horizontal or vertical).