Uplofile is open sourceStar on GitHub

Quick Start

Build a fully functional file uploader in minutes.

Complete Example

Here's a complete example with styling, validation, and upload handling:

1import {
2 UplofileRoot,
3 UplofileDropzone,
4 UplofileTrigger,
5 UplofilePreview,
6 type UploadFileItem,
7} from "@/components/ui/uplofile";
8
9const onRemove = async (item: UploadFileItem, signal: AbortSignal) => {
10 // remove logic
11};
12
13const upload = async (
14 file: File,
15 signal: AbortSignal,
16 onProgress?: (progress: number) => void,
17) => {
18 const formData = new FormData();
19 formData.append("file", file);
20
21 const res = await fetch("/api/upload", {
22 method: "POST",
23 body: formData,
24 signal,
25 });
26
27 // const res = await axios.post('/api/upload', formData, {
28 // onUploadProgress: onProgress
29 // });
30
31 if (!res.ok) throw new Error("Upload failed");
32 return await res.json();
33};
34
35function FileUploader() {
36 return (
37 <UplofileRoot upload={upload} removeMode="strict" onRemove={onRemove}>
38 <UplofileDropzone className="border-2 border-dashed rounded-lg p-8">
39 <span>Drop files here or </span>
40 <UplofileTrigger className="underline text-blue-500 cursor-pointer">
41 Select files
42 </UplofileTrigger>
43 <div className="border-t my-6 py-6">
44 <UplofilePreview />
45 </div>
46 </UplofileDropzone>
47 </UplofileRoot>
48 );
49}

With Pre-hydrated Files

If you have existing files (e.g., when editing), pass them as initial values:

1<UplofileRoot
2 initial={[
3 { id: '1', name: 'existing-file.jpg', url: '/uploads/existing-file.jpg' },
4 { id: '2', name: 'another-file.pdf', url: '/uploads/another-file.pdf' },
5 ]}
6>
7 {/* ... */}
8</UplofileRoot>

Server-side Delete

For strict deletion that requires server confirmation:

1<UplofileRoot
2 removeMode="strict"
3 onRemove={async (item, signal) => {
4 const res = await fetch(`/api/files/${item.id}`, {
5 method: 'DELETE',
6 signal,
7 });
8 if (!res.ok) throw new Error('Delete failed');
9 }}
10>
11 {/* ... */}
12</UplofileRoot>