All components

File Upload

A component for uploading files

Default
Default file upload

Upload your files

Drag and drop or click to browse

Files up to 5.0 MB · Maximum 5 files

1"use client";
2
3import { FileUpload } from "@/registry/ui";
4
5export function FileUploadDefaultExample() {
6 return (
7 <div className="mx-auto w-full max-w-2xl">
8 <FileUpload
9 maxFiles={5}
10 maxConcurrentUploads={2}
11 onUpload={async (_, onProgress, signal) => {
12 return new Promise<void>((resolve, reject) => {
13 let progress = 0;
14
15 const interval = window.setInterval(() => {
16 if (signal.aborted) {
17 window.clearInterval(interval);
18 reject(new DOMException("Upload cancelled", "AbortError"));
19 return;
20 }
21
22 progress = Math.min(progress + 10, 100);
23 onProgress(progress);
24
25 if (progress === 100) {
26 window.clearInterval(interval);
27 resolve();
28 }
29 }, 200);
30 });
31 }}
32 />
33 </div>
34 );
35}
Edge Cases
File upload with edge cases
In-progress upload
Demo only - uploads stall at 88% and never complete.

Upload your files

Drag and drop or click to browse

Files up to 5.0 MB · Maximum 3 files

Error and retry
The first attempt fails at 60%. Press retry to complete it.

Upload your files

Drag and drop or click to browse

Files up to 5.0 MB · Maximum 3 files

1"use client";
2
3import { useRef } from "react";
4
5import {
6 Card,
7 CardContent,
8 CardDescription,
9 CardHeader,
10 CardTitle,
11} from "@/components/ui/card";
12import { FileUpload } from "@/registry/ui";
13
14type UploadHandler = (
15 file: File,
16 onProgress: (progress: number) => void,
17 signal: AbortSignal,
18) => Promise<void>;
19
20function simulateUpload({
21 onProgress,
22 signal,
23 shouldFail = false,
24}: {
25 onProgress: (progress: number) => void;
26 signal: AbortSignal;
27 shouldFail?: boolean;
28}) {
29 return new Promise<void>((resolve, reject) => {
30 let progress = 0;
31
32 const interval = window.setInterval(() => {
33 if (signal.aborted) {
34 window.clearInterval(interval);
35 reject(new DOMException("Upload cancelled", "AbortError"));
36 return;
37 }
38
39 progress = Math.min(progress + 10, 100);
40 onProgress(progress);
41
42 if (shouldFail && progress >= 60) {
43 window.clearInterval(interval);
44 reject(new Error("Simulated upload failure"));
45 return;
46 }
47
48 if (progress === 100) {
49 window.clearInterval(interval);
50 resolve();
51 }
52 }, 180);
53 });
54}
55
56export function FileUploadEdgeCasesExample() {
57 const attemptsRef = useRef(new Map<string, number>());
58
59 const handleIndefiniteUpload: UploadHandler = async (
60 _,
61 onProgress,
62 signal,
63 ) => {
64 // Demo-only: stalls below completion so the uploading UI stays visible.
65 let progress = 0;
66 onProgress(progress);
67
68 await new Promise<void>((_, reject) => {
69 const interval = window.setInterval(() => {
70 if (signal.aborted) {
71 window.clearInterval(interval);
72 reject(new DOMException("Upload cancelled", "AbortError"));
73 return;
74 }
75
76 progress = Math.min(progress + 4, 88);
77 onProgress(progress);
78 }, 200);
79 });
80 };
81
82 const handleRetryUpload: UploadHandler = async (file, onProgress, signal) => {
83 const fileKey = `${file.name}-${file.size}-${file.lastModified}`;
84 const attempts = attemptsRef.current.get(fileKey) ?? 0;
85
86 attemptsRef.current.set(fileKey, attempts + 1);
87
88 await simulateUpload({
89 onProgress,
90 signal,
91 shouldFail: attempts === 0,
92 });
93 };
94
95 return (
96 <div className="grid gap-6 lg:grid-cols-2">
97 <Card>
98 <CardHeader>
99 <CardTitle className="text-base">In-progress upload</CardTitle>
100 <CardDescription>
101 Demo only - uploads stall at 88% and never complete.
102 </CardDescription>
103 </CardHeader>
104
105 <CardContent>
106 <FileUpload
107 maxFiles={3}
108 maxConcurrentUploads={2}
109 onUpload={handleIndefiniteUpload}
110 />
111 </CardContent>
112 </Card>
113
114 <Card>
115 <CardHeader>
116 <CardTitle className="text-base">Error and retry</CardTitle>
117 <CardDescription>
118 The first attempt fails at 60%. Press retry to complete it.
119 </CardDescription>
120 </CardHeader>
121
122 <CardContent>
123 <FileUpload
124 maxFiles={3}
125 maxConcurrentUploads={1}
126 onUpload={handleRetryUpload}
127 />
128 </CardContent>
129 </Card>
130 </div>
131 );
132}

Installation & source

Install via the shadcn CLI or copy the registry files manually.

bash
npx shadcn@latest add @tt-ui/file-upload

Props

NameTypeDefaultDescription
onUpload() => voidundefinedCallback when a file is uploaded
acceptstringimage/*,application/pdfThe accept attribute for the file input
maxSizenumber5_000_000The maximum size of the file in bytes
maxFilesnumberundefinedThe maximum number of files to upload
maxConcurrentUploadsnumberundefinedThe maximum number of concurrent uploads
classNamestringundefinedThe class name of the file upload