Overlays
Interactive Folder
#folder#3d#file
Click a folder to open it
Projects
Design
implementedInfinityUI component
This component is fully implemented in InfinityUI and wired into the docs and registry flow.
Installation
Use the registry command to add the component source, and install any package dependencies if needed.
Infinity Registry
Install the component source into your project with the shadcn CLI.
npx shadcn@latest add https://infinityui-pearl.vercel.app/r/folder-previewPackage Dependencies
Install the npm packages used by this component source.
npm install @iconify/react framer-motionComponent Code
Copy and paste this code into your component file.
tsx
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Icon } from "@iconify/react";
interface FolderPreviewProps {
folderName?: string;
items?: string[];
color?: string;
}
export function FolderPreview({
folderName = "Projects",
items = ["resume.pdf", "portfolio.fig", "cover-letter.docx", "references.pdf"],
color = "#6366f1",
}: FolderPreviewProps) {
const [open, setOpen] = useState(false);
return (
<div className="relative w-48 select-none">
<motion.div
whileHover={{ y: -4 }}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
className="relative cursor-pointer"
onClick={() => setOpen(!open)}
>
<div
className="absolute -top-3 left-3 h-4 w-16 rounded-t-lg"
style={{ backgroundColor: color }}
/>
<div
className="relative flex h-36 w-full flex-col items-center justify-center gap-2 rounded-tl-none rounded-tr-xl rounded-bl-xl rounded-br-xl border border-white/10"
style={{ backgroundColor: color + "22", borderColor: color + "44" }}
>
{[...Array(3)].map((_, i) => (
<motion.div
key={i}
className="absolute bottom-2 h-32 w-28 rounded-lg border border-white/10 bg-zinc-800"
animate={{
rotate: open ? (i - 1) * 12 : 0,
y: open ? -(i * 8) : 0,
}}
transition={{
type: "spring",
stiffness: 300,
damping: 25,
delay: i * 0.05,
}}
style={{ zIndex: i }}
>
<div className="flex flex-col gap-1 p-2">
{[0, 1, 2].map((j) => (
<div
key={j}
className="h-1.5 rounded-full bg-white/10"
style={{ width: `${60 + j * 15}%` }}
/>
))}
</div>
</motion.div>
))}
<Icon
icon="solar:folder-bold-duotone"
fontSize={32}
style={{ color }}
className="relative z-10"
/>
</div>
</motion.div>
<p className="mt-2 text-center text-sm font-medium text-zinc-400">
{folderName}
</p>
<AnimatePresence>
{open && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 8, scale: 0.95 }}
className="absolute left-1/2 top-full z-30 mt-3 w-52 -translate-x-1/2 overflow-hidden rounded-xl border border-white/10 bg-zinc-900 shadow-2xl"
>
{items.map((file) => (
<div
key={file}
className="flex cursor-pointer items-center gap-3 px-4 py-2.5 transition-colors hover:bg-white/5"
>
<Icon
icon="solar:file-linear"
className="shrink-0 text-zinc-500"
fontSize={14}
/>
<span className="truncate text-xs text-zinc-300">
{file}
</span>
</div>
))}
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default FolderPreview;