Uplofile is open sourceStar on GitHub

Image Gallery Uploader

An image-focused uploader with thumbnail previews in a grid layout.

Preview

Code

1import {
2 UplofileRoot,
3 UplofileDropzone,
4 UplofileTrigger,
5 UplofilePreview,
6 type UploadFileItem,
7} from "@/components/ui/uplofile";
8import {
9 IoAddOutline,
10 IoImageOutline,
11 IoReloadOutline,
12 IoCheckmarkCircleOutline,
13 IoAlertCircleOutline,
14} from "react-icons/io5";
15import { mockUpload } from "@/lib/utils.ts";
16
17export default function ImageGalleryDemo() {
18 return (
19 <UplofileRoot upload={mockUpload} accept="image/*" multiple>
20 <UplofilePreview
21 render={({ items }) => (
22 <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
23 {items.map((item) => (
24 <ImageItem key={item.uid} item={item} />
25 ))}
26
27 <UplofileDropzone className="group aspect-square rounded-xl flex flex-col items-center justify-center border-2 border-dashed border-muted-foreground/25 hover:border-primary/50 hover:bg-primary/5 cursor-pointer transition-all duration-200 data-[dragging=true]:border-primary data-[dragging=true]:bg-primary/10 data-[dragging=true]:scale-95">
28 <UplofileTrigger>
29 <div className="flex flex-col items-center gap-2 text-muted-foreground group-hover:text-primary transition-colors">
30 <div className="p-3 rounded-full bg-muted group-hover:bg-primary/10 transition-colors">
31 <IoAddOutline className="h-6 w-6" />
32 </div>
33 <span className="text-[10px] font-bold uppercase tracking-wider">
34 Add Image
35 </span>
36 </div>
37 </UplofileTrigger>
38 </UplofileDropzone>
39 </div>
40 )}
41 />
42 </UplofileRoot>
43 );
44}
45
46function ImageItem({ item }: { item: UploadFileItem }) {
47 return (
48 <div className="group relative aspect-square rounded-xl overflow-hidden bg-white border shadow-sm animate-in fade-in zoom-in-95 duration-200">
49 {item.previewUrl ? (
50 <img
51 src={item.previewUrl}
52 alt={item.name}
53 className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
54 />
55 ) : (
56 <div className="w-full h-full flex items-center justify-center bg-muted/50">
57 <IoImageOutline className="h-6 w-6 text-muted-foreground/40" />
58 </div>
59 )}
60
61 {item.status === "uploading" && (
62 <div className="absolute inset-0 bg-black/60 backdrop-blur-[2px] flex flex-col items-center justify-center p-2">
63 <IoReloadOutline className="h-6 w-6 text-white animate-spin mb-2" />
64 <div className="w-full bg-white/20 rounded-full h-1 max-w-[40px]">
65 <div
66 className="bg-white h-full rounded-full transition-all duration-300"
67 style={{ width: `${item.progress}%` }}
68 />
69 </div>
70 </div>
71 )}
72
73 {item.status === "done" && (
74 <div className="absolute top-2 right-2 bg-emerald-500 text-white p-1 rounded-full shadow-lg scale-0 group-hover:scale-100 transition-transform duration-200">
75 <IoCheckmarkCircleOutline className="h-3 w-3" />
76 </div>
77 )}
78
79 {item.status === "error" && (
80 <div className="absolute inset-0 bg-destructive/60 backdrop-blur-[1px] flex flex-col items-center justify-center p-2 text-white animate-in zoom-in-95">
81 <IoAlertCircleOutline className="h-6 w-6 mb-1" />
82 <span className="text-[10px] font-bold uppercase tracking-wider">
83 Failed
84 </span>
85 </div>
86 )}
87
88 <div className="absolute inset-0 bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" />
89 </div>
90 );
91}

Key Points

  • Uses accept="image/*" to filter file types
  • Grid layout with thumbnail previews via item.previewUrl
  • Add button appears alongside existing images
  • Progress overlay during upload