Uplofile is open sourceStar on GitHub

Dropzone Uploader

A drag-and-drop zone with visual feedback when files are dragged over.

Preview

Code

1import {
2 UplofileRoot,
3 UplofileDropzone,
4 UplofileTrigger,
5 UplofilePreview,
6 type UploadFileItem,
7} from "@/components/ui/uplofile";
8import { IoCloudUploadOutline, IoDocumentOutline, IoCloseOutline, IoCheckmarkCircleOutline, IoReloadOutline } from "react-icons/io5";
9import { mockUpload } from "@/lib/utils.ts";
10
11export default function DropzoneUploaderDemo() {
12 return (
13 <UplofileRoot upload={mockUpload} accept="*/*" multiple>
14 <UplofileDropzone className="group relative border-2 border-dashed border-muted-foreground/25 rounded-xl p-12 text-center hover:border-primary/50 transition-all duration-200 data-[dragging=true]:border-primary data-[dragging=true]:bg-primary/5 data-[dragging=true]:scale-[1.01]">
15 <UplofileTrigger>
16 <div className="flex flex-col items-center gap-4 cursor-pointer">
17 <div className="p-4 rounded-full bg-primary/10 text-primary group-hover:scale-110 transition-transform duration-200">
18 <IoCloudUploadOutline className="h-8 w-8" />
19 </div>
20 <div className="grid gap-1">
21 <p className="text-sm font-semibold">
22 Click to upload or drag and drop
23 </p>
24 <p className="text-xs text-muted-foreground">
25 Any file type
26 </p>
27 </div>
28 </div>
29 </UplofileTrigger>
30 </UplofileDropzone>
31
32 <UplofilePreview
33 render={({ items }) => (
34 <div className="mt-8 space-y-3">
35 {items.map((item) => (
36 <DropzoneFileItem key={item.uid} item={item} />
37 ))}
38 </div>
39 )}
40 />
41 </UplofileRoot>
42 );
43}
44
45function DropzoneFileItem({ item }: { item: UploadFileItem }) {
46 return (
47 <div className="flex items-center gap-4 p-3 rounded-xl border bg-card shadow-sm animate-in fade-in slide-in-from-bottom-2">
48 <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
49 <IoDocumentOutline className="h-5 w-5" />
50 </div>
51 <div className="grid flex-1 gap-1 overflow-hidden">
52 <div className="flex items-center justify-between gap-2">
53 <span className="text-sm font-medium truncate">{item.name}</span>
54 <span className="text-xs text-muted-foreground whitespace-nowrap">
55 {item.status === "uploading" ? `${item.progress}%` : item.status}
56 </span>
57 </div>
58 <div className="relative h-1.5 w-full overflow-hidden rounded-full bg-secondary">
59 <div
60 className="h-full bg-primary transition-all duration-300"
61 style={{
62 width: item.status === "done" ? "100%" : `${item.progress}%`,
63 }}
64 />
65 </div>
66 </div>
67 <div className="flex items-center gap-2">
68 {item.status === "uploading" && (
69 <IoReloadOutline className="h-4 w-4 animate-spin text-muted-foreground" />
70 )}
71 {item.status === "done" && (
72 <IoCheckmarkCircleOutline className="h-4 w-4 text-emerald-500" />
73 )}
74 {item.status === "error" && <IoCloseOutline className="h-4 w-4 text-destructive" />}
75 </div>
76 </div>
77 );
78}

Key Points

  • Uses data-[dragging=true] for visual feedback on drag
  • Combines dropzone with clickable trigger
  • multiple prop enables multi-file selection