Components
Loading preview...
The Stacked Cards Upload component is a modern, interactive file upload interface built with Shadcn UI and Lucide icons. Each uploaded file or folder appears as a card in a stacked layout, animating smoothly into position as new files are added. Upload progress is visually represented as a subtle filling background inside the card, designed to adapt seamlessly to both light and dark themes. The file name and remove icon are vertically centered for a clean, consistent look, and users can remove any file with a single click on the trash icon. Fully configurable with callbacks like onRemove, this component provides a user-friendly, visually appealing way to manage multiple uploads in modern web applications.
npx shadcn@latest add https://21st.dev/r/ruixen.ui/stacked-cards-upload"use client"
import * as React from "react"
import { StackedCardsUpload, UploadFile } from "@/components/ui/stacked-cards-upload"
import { Button } from "@/components/ui/button"
import { v4 as uuidv4 } from "uuid"
export default function DemoStackedCardsUpload() {
const [files, setFiles] = React.useState<UploadFile[]>([])
const inputRef = React.useRef<HTMLInputElement | null>(null)
// Fake upload simulation
React.useEffect(() => {
const interval = setInterval(() => {
setFiles((prev) =>
prev.map((f) =>
f.status === "uploading"
? {
...f,
progress: Math.min(f.progress + 10, 100),
status: f.progress + 10 >= 100 ? "done" : "uploading",
}
: f
)
)
}, 800)
return () => clearInterval(interval)
}, [])
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return
const newFiles: UploadFile[] = Array.from(e.target.files).map((file) => ({
id: uuidv4(),
file,
progress: 0,
status: "uploading",
}))
setFiles((prev) => [...prev, ...newFiles])
}
const handleRemove = (id: string) => {
setFiles((prev) => prev.filter((f) => f.id !== id))
}
return (
<div className="p-6 space-y-6">
<h1 className="text-2xl font-semibold">Stacked Cards Upload Demo</h1>
<input
type="file"
multiple
className="hidden"
ref={inputRef}
onChange={handleFileSelect}
/>
<Button onClick={() => inputRef.current?.click()}>Upload Files</Button>
<StackedCardsUpload files={files} onRemove={handleRemove} />
</div>
)
}