Uplofile is open sourceStar on GitHub

File List with Actions

A detailed file list with file info, progress indicators, and remove buttons.

Preview

No files added yet.

Code

1import { clsx } from "clsx";
2import {
3 UplofileRoot,
4 UplofileTrigger,
5 UplofilePreview,
6 UplofileRemove,
7 type UploadFileItem,
8} from "@/components/ui/uplofile";
9import {
10 IoDocumentOutline,
11 IoImageOutline,
12 IoDocumentTextOutline,
13 IoArchiveOutline,
14 IoCloseOutline,
15 IoAddOutline,
16 IoCheckmarkCircleOutline,
17 IoAlertCircleOutline,
18} from "react-icons/io5";
19import { mockUpload, formatBytes } from "@/lib/utils.ts";
20
21export default function FileListWithActionsDemo() {
22 return (
23 <UplofileRoot upload={mockUpload} multiple>
24 <UplofileTrigger asChild>
25 <button className="inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-all shadow-sm font-medium text-sm active:scale-95">
26 <IoAddOutline className="h-4 w-4" />
27 Add Files
28 </button>
29 </UplofileTrigger>
30
31 <UplofilePreview
32 render={({ items }) => (
33 <div className="mt-6 divide-y divide-border border rounded-xl bg-card shadow-sm overflow-hidden">
34 {items.length === 0 && (
35 <div className="p-12 text-center">
36 <IoDocumentOutline className="h-10 w-10 text-muted-foreground/20 mx-auto mb-4" />
37 <p className="text-sm text-muted-foreground">
38 No files added yet.
39 </p>
40 </div>
41 )}
42 {items.map((item) => (
43 <FileItem key={item.uid} item={item} />
44 ))}
45 </div>
46 )}
47 />
48 </UplofileRoot>
49 );
50}
51
52function FileItem({ item }: { item: UploadFileItem }) {
53 return (
54 <div className="flex items-center gap-4 p-4 hover:bg-muted/50 transition-colors animate-in fade-in slide-in-from-left-2">
55 <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
56 {getFileIcon(item.name)}
57 </div>
58 <div className="flex-1 min-w-0">
59 <div className="flex items-center justify-between gap-2 mb-1">
60 <p className="text-sm font-semibold truncate text-foreground">
61 {item.name}
62 </p>
63 <span className="text-[10px] font-bold text-muted-foreground uppercase tracking-widest">
64 {item.file ? formatBytes(item.file.size) : "—"}
65 </span>
66 </div>
67
68 <div className="flex items-center gap-2">
69 {item.status === "uploading" ? (
70 <div className="flex items-center gap-2 w-full">
71 <div className="flex-1 h-1 bg-secondary rounded-full overflow-hidden">
72 <div
73 className="h-full bg-primary transition-all duration-300"
74 style={{ width: `${item.progress}%` }}
75 />
76 </div>
77 <span className="text-[10px] font-medium tabular-nums">
78 {item.progress}%
79 </span>
80 </div>
81 ) : (
82 <div className="flex items-center gap-1.5">
83 {item.status === "done" && (
84 <IoCheckmarkCircleOutline className="h-3 w-3 text-emerald-500" />
85 )}
86 {item.status === "error" && (
87 <IoAlertCircleOutline className="h-3 w-3 text-destructive" />
88 )}
89 <span
90 className={clsx(
91 "text-[10px] font-bold uppercase tracking-tighter italic",
92 item.status === "done" && "text-emerald-600",
93 item.status === "error" && "text-destructive",
94 item.status === "idle" && "text-muted-foreground",
95 )}
96 >
97 {item.status}
98 </span>
99 </div>
100 )}
101 </div>
102 </div>
103 <UplofileRemove
104 uid={item.uid}
105 className="p-2 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-all"
106 >
107 <IoCloseOutline className="h-4 w-4" />
108 </UplofileRemove>
109 </div>
110 );
111}
112
113function getFileIcon(name: string) {
114 const ext = name.split(".").pop()?.toLowerCase();
115 if (["jpg", "jpeg", "png", "gif", "webp"].includes(ext || ""))
116 return <IoImageOutline className="h-5 w-5" />;
117 if (ext === "pdf" || ["doc", "docx"].includes(ext || ""))
118 return <IoDocumentTextOutline className="h-5 w-5" />;
119 if (["zip", "rar", "7z"].includes(ext || ""))
120 return <IoArchiveOutline className="h-5 w-5" />;
121 return <IoDocumentOutline className="h-5 w-5" />;
122}

Key Points

  • Uses UplofileRemove component with uid prop
  • File type icons based on extension
  • Human-readable file sizes
  • Status shown inline (uploading %, done, error)