Circle Menu
NewDialog Form
NewFlip Scroll
NewHorizontal Scroll
NewImage Pile
Interactive CTA
NewInteractive Folder
NewInterest Picker
NewJelly Loader
Mask Cursor Effect
Magnet Tabs
Masonry Grid
OTP Input
NewPhoto Gallery
NewPixelated Carousel
NewRubik Cube
NewSidebar
NewSine Wave
NewSkeumorphic Music Card
Stacked Input Form
NewStack Scroll
NewTrading Card
Sidebar
A collapsible navigation sidebar with smooth expand/collapse animations and hover previews. Features icon-based navigation with animated content panels that slide in and out seamlessly.
Install dependencies
npm i framer-motion lucide-react
Utility function
Create a file lib/utils.ts and paste this code
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Component
Create a file sidebar.tsx in your components folder and paste this code
'use client';
import { Cloud, Folder, Home, Menu, PanelLeftClose, PanelsTopLeft, Star } from 'lucide-react';
import React, { useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { cn } from '@/lib/utils';
const HomeChildren = () => {
return (
<div className="w-full h-full bg-zinc-900 p-6">
<div className="mb-8">
<div className="h-8 bg-zinc-700 rounded w-24 animate-pulse"></div>
</div>
<div className="space-y-3 mb-8">
<div className="w-full bg-gray-600 px-4 py-3 rounded-lg flex items-center gap-3">
<div className="h-4 bg-gray-500 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-gray-500 rounded w-36 animate-pulse"></div>
</div>
</div>
<div className="mb-6">
<div className="flex items-center justify-between mb-3">
<div className="h-4 bg-zinc-700 rounded w-12 animate-pulse"></div>
<div className="h-4 bg-gray-500 rounded w-4 animate-pulse"></div>
</div>
<div className="space-y-2">
<div className="w-full bg-purple-800 px-4 py-3 rounded-lg flex items-center gap-3">
<div className="h-4 bg-purple-600 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-purple-600 rounded w-20 animate-pulse"></div>
</div>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-3">
<div className="h-4 bg-zinc-700 rounded w-16 animate-pulse"></div>
</div>
<div className="space-y-2">
<div className="w-full hover:bg-zinc-800 py-3 rounded-lg flex items-center gap-3">
<div className="h-4 bg-zinc-600 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-40 animate-pulse"></div>
</div>
</div>
</div>
</div>
);
};
const ProjectsChildren = () => {
return (
<div className="w-full h-full bg-zinc-800 p-6">
<div className="mb-8">
<div className="h-6 bg-blue-600 rounded w-32 animate-pulse mb-2"></div>
<div className="h-4 bg-zinc-700 rounded w-48 animate-pulse"></div>
</div>
<div className="grid grid-cols-2 gap-4 mb-6">
<div className="bg-zinc-900 rounded-lg p-4 border-zinc-700">
<div className="h-4 bg-zinc-600 rounded w-20 animate-pulse mb-2"></div>
<div className="h-3 bg-zinc-700 rounded w-16 animate-pulse"></div>
</div>
<div className="bg-zinc-900 rounded-lg p-4 border-zinc-700">
<div className="h-4 bg-zinc-600 rounded w-24 animate-pulse mb-2"></div>
<div className="h-3 bg-zinc-700 rounded w-20 animate-pulse"></div>
</div>
<div className="bg-zinc-900 rounded-lg p-4 border-zinc-700">
<div className="h-4 bg-zinc-600 rounded w-18 animate-pulse mb-2"></div>
<div className="h-3 bg-zinc-700 rounded w-14 animate-pulse"></div>
</div>
<div className="bg-zinc-900 rounded-lg p-4 border-zinc-700">
<div className="h-4 bg-zinc-600 rounded w-22 animate-pulse mb-2"></div>
<div className="h-3 bg-zinc-700 rounded w-18 animate-pulse"></div>
</div>
</div>
<div>
<div className="h-4 bg-zinc-700 rounded w-24 animate-pulse mb-3"></div>
<div className="space-y-2">
<div className="flex items-center gap-3 p-2 hover:bg-zinc-800 rounded">
<div className="h-3 bg-zinc-600 rounded-full w-3 animate-pulse"></div>
<div className="h-3 bg-zinc-600 rounded w-32 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-2 hover:bg-zinc-800 rounded">
<div className="h-3 bg-zinc-600 rounded-full w-3 animate-pulse"></div>
<div className="h-3 bg-zinc-600 rounded w-28 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-2 hover:bg-zinc-800 rounded">
<div className="h-3 bg-zinc-600 rounded-full w-3 animate-pulse"></div>
<div className="h-3 bg-zinc-600 rounded w-36 animate-pulse"></div>
</div>
</div>
</div>
</div>
);
};
const TemplatesChildren = () => {
return (
<div className="w-full h-full bg-zinc-900 p-6">
<div className="mb-8">
<div className="h-6 bg-green-600 rounded w-28 animate-pulse mb-2"></div>
<div className="h-4 bg-zinc-700 rounded w-40 animate-pulse"></div>
</div>
<div className="space-y-4 mb-6">
<div className="flex items-center gap-3 p-3 bg-zinc-800 rounded-lg">
<div className="h-4 bg-zinc-600 rounded-full w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-24 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-3 bg-zinc-800 rounded-lg">
<div className="h-4 bg-zinc-600 rounded-full w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-20 animate-pulse"></div>
</div>
</div>
<div>
<div className="h-4 bg-zinc-700 rounded w-32 animate-pulse mb-3"></div>
<div className="space-y-2">
<div className="h-16 bg-gradient-to-r from-pink-500/20 to-purple-500/20 rounded-lg animate-pulse"></div>
<div className="h-16 bg-gradient-to-r from-blue-500/20 to-cyan-500/20 rounded-lg animate-pulse"></div>
<div className="h-16 bg-gradient-to-r from-green-500/20 to-emerald-500/20 rounded-lg animate-pulse"></div>
</div>
</div>
</div>
);
};
const AIChildren = () => {
return (
<div className="w-full h-full bg-zinc-800 p-6">
<div className="mb-8">
<div className="h-6 bg-purple-600 rounded w-20 animate-pulse mb-2"></div>
<div className="h-4 bg-zinc-700 rounded w-36 animate-pulse"></div>
</div>
<div className="grid grid-cols-2 gap-3 mb-6">
<div className="bg-gradient-to-br from-purple-600/20 to-pink-600/20 rounded-lg p-4">
<div className="h-5 bg-purple-500/50 rounded w-5 animate-pulse mb-2"></div>
<div className="h-3 bg-purple-500/50 rounded w-16 animate-pulse"></div>
</div>
<div className="bg-gradient-to-br from-blue-600/20 to-cyan-600/20 rounded-lg p-4">
<div className="h-5 bg-blue-500/50 rounded w-5 animate-pulse mb-2"></div>
<div className="h-3 bg-blue-500/50 rounded w-20 animate-pulse"></div>
</div>
<div className="bg-gradient-to-br from-green-600/20 to-emerald-600/20 rounded-lg p-4 ">
<div className="h-5 bg-green-500/50 rounded w-5 animate-pulse mb-2"></div>
<div className="h-3 bg-green-500/50 rounded w-18 animate-pulse"></div>
</div>
<div className="bg-gradient-to-br from-orange-600/20 to-red-600/20 rounded-lg p-4">
<div className="h-5 bg-orange-500/50 rounded w-5 animate-pulse mb-2"></div>
<div className="h-3 bg-orange-500/50 rounded w-22 animate-pulse"></div>
</div>
</div>
<div>
<div className="h-4 bg-zinc-700 rounded w-28 animate-pulse mb-3"></div>
<div className="space-y-2">
<div className="flex items-center gap-3 p-2 bg-zinc-900 rounded">
<div className="h-3 bg-zinc-600 rounded w-40 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-2 bg-zinc-900 rounded">
<div className="h-3 bg-zinc-600 rounded w-36 animate-pulse"></div>
</div>
</div>
</div>
</div>
);
};
const CloudChildren = () => {
return (
<div className="w-full h-full bg-zinc-900 p-6">
<div className="mb-8">
<div className="h-6 bg-cyan-600 rounded w-24 animate-pulse mb-2"></div>
<div className="h-4 bg-zinc-700 rounded w-32 animate-pulse"></div>
</div>
<div className="bg-zinc-800 rounded-lg p-4 mb-6">
<div className="flex items-center justify-between mb-3">
<div className="h-4 bg-zinc-600 rounded w-20 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-16 animate-pulse"></div>
</div>
<div className="w-full bg-zinc-700 rounded-full h-2 mb-2">
<div className="bg-cyan-500 h-2 rounded-full w-3/4 animate-pulse"></div>
</div>
<div className="h-3 bg-zinc-600 rounded w-24 animate-pulse"></div>
</div>
<div className="space-y-3 mb-6">
<div className="flex items-center gap-3 p-3 bg-zinc-800 rounded-lg hover:bg-zinc-700 transition-colors">
<div className="h-4 bg-green-500/50 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-20 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-3 bg-zinc-800 rounded-lg hover:bg-zinc-700 transition-colors">
<div className="h-4 bg-blue-500/50 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-24 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-3 bg-zinc-800 rounded-lg hover:bg-zinc-700 transition-colors">
<div className="h-4 bg-purple-500/50 rounded w-4 animate-pulse"></div>
<div className="h-4 bg-zinc-600 rounded w-28 animate-pulse"></div>
</div>
</div>
<div>
<div className="h-4 bg-zinc-700 rounded w-24 animate-pulse mb-3"></div>
<div className="space-y-2">
<div className="flex items-center gap-3 p-2 hover:bg-zinc-800 rounded">
<div className="h-4 bg-cyan-500/50 rounded w-4 animate-pulse"></div>
<div className="h-3 bg-zinc-600 rounded w-32 animate-pulse"></div>
</div>
<div className="flex items-center gap-3 p-2 hover:bg-zinc-800 rounded">
<div className="h-4 bg-gray-500/50 rounded w-4 animate-pulse"></div>
<div className="h-3 bg-zinc-600 rounded w-28 animate-pulse"></div>
</div>
</div>
</div>
</div>
);
};
const SidebarToggle = ({
isOpen,
setIsOpen
}: {
isOpen: string | false;
setIsOpen: (isOpen: string | false) => void;
}) => {
const renderIcon = () => {
switch (isOpen !== false) {
case true:
return <PanelLeftClose size={24} />;
case false:
return <Menu size={24} />;
}
};
return (
<li
onClick={() => {
if (isOpen) {
setIsOpen(false);
} else {
setIsOpen('Home');
}
}}
className="flex flex-col items-center justify-center gap-1 mb-2 cursor-pointer hover:bg-zinc-800 my-3 py-2.5 mx-4 rounded-md m-1.5"
>
<AnimatePresence mode="popLayout" initial={false}>
<motion.span
initial={{ opacity: 0.3, scale: 0.5, filter: 'blur(4px)' }}
animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
exit={{ opacity: 0.3, scale: 0.5, filter: 'blur(4px)' }}
key={`sidebar-toggle-${isOpen ? true : false}`}
>
{renderIcon()}
</motion.span>
</AnimatePresence>
</li>
);
};
const Sidebar = () => {
const [isOpen, setIsOpen] = useState<string | false>(false);
const [hovering, setHovering] = useState<string | null>(null);
const options = [
{
name: 'Home',
icon: <Home size={24} />,
children: <HomeChildren />
},
{
name: 'Projects',
icon: <Folder size={24} />,
children: <ProjectsChildren />
},
{
name: 'Templates',
icon: <PanelsTopLeft size={24} />,
children: <TemplatesChildren />
},
{
name: 'AI',
icon: <Star size={24} />,
children: <AIChildren />
},
{
name: 'Cloud',
icon: <Cloud size={24} />,
children: <CloudChildren />
}
];
return (
<div className="h-full flex items-start">
<ul className="h-full w-20 bg-zinc-900 flex flex-col gap-1 border-r border-zinc-800">
<SidebarToggle isOpen={isOpen} setIsOpen={setIsOpen} />
{options.map((option) => {
const isActive = isOpen === option.name;
return (
<li
onMouseEnter={() => {
if (!isOpen) setHovering(option.name);
}}
onMouseLeave={(e) => {
if (!isOpen) {
setHovering(null);
}
}}
onClick={() => {
setIsOpen(option.name);
}}
className="group px-1.5 gap-1 py-1 flex flex-col items-center justify-center cursor-pointer select-none"
>
<div
className={cn(
'flex flex-col items-center justify-center gap-1 group-hover:bg-zinc-800 p-2.5 rounded-md text-white/90',
isActive && 'bg-zinc-800 text-blue-400'
)}
>
{option.icon}
</div>
<p className="text-xs">{option.name}</p>
</li>
);
})}
</ul>
<AnimatePresence mode="popLayout" initial={false}>
{(hovering || isOpen) && (
<motion.section
id="sidebar-children"
initial={{ clipPath: 'inset(100% 0 0 0)' }}
animate={{ clipPath: 'inset(0 0 0 0)' }}
exit={{ clipPath: 'inset(0 0 100% 0)', transition: { delay: 0.06 } }}
transition={{
duration: 0.2,
type: 'spring',
bounce: 0
}}
onMouseEnter={() => {
if (!isOpen) setHovering(isOpen || hovering);
}}
onMouseLeave={() => {
if (!isOpen) setHovering(null);
}}
key={isOpen ? isOpen : hovering}
className="h-full bg-zinc-900"
>
<div className="w-[300px] h-full">
{(isOpen || hovering) &&
options.find((opt) => opt.name === (isOpen || hovering))?.children}
</div>
</motion.section>
)}
</AnimatePresence>
</div>
);
};
export default Sidebar;
Usage
<Sidebar />