|$ curl https://forge-ai.dev/api/markdown?path=docs/components/file-upload
$cat docs/file-upload.md
updated Recently·16 min read·published

File Upload

CSSHTMLUIAdvanced🎯Free Tools

File upload components from basic click-to-browse zones to advanced drag-and-drop areas with multi-file support, image previews, progress tracking, and client-side validation.

Basic Upload Zone

A click-to-browse upload zone with a dashed border and icon. Clicking the zone opens the native file picker. The <input type="file"> is visually hidden but remains accessible. The zone highlights on drag over for visual feedback.

basic-upload
Live
untitled.html
HTML
1<div class="upload-zone" id="upload-zone-1">
2 <input type="file" class="file-input" id="file-1" multiple/>
3 <label for="file-1" class="upload-label">
4 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32">
5 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
6 <polyline points="17 8 12 3 7 8"/>
7 <line x1="12" y1="3" x2="12" y2="15"/>
8 </svg>
9 <span class="upload-title">
10 Drop files here
11 </span>
12 <span class="upload-sub">
13 or click to browse · Max 10MB per file
14 </span>
15 </label>
16</div>
preview
Drag & Drop Events

The drag-and-drop API fires a sequence of events on the drop zone:dragenter, dragover, dragleave, and drop. Both dragenter and dragover must call preventDefault() to allow dropping.

drag-drop-events.js
JavaScript
1// Highlight on drag enter/over
2['dragenter', 'dragover'].forEach(evt => {
3 zone.addEventListener(evt, (e) => {
4 e.preventDefault();
5 zone.classList.add('dragover');
6 });
7});
8
9// Remove highlight on drag leave/drop
10['dragleave', 'drop'].forEach(evt => {
11 zone.addEventListener(evt, (e) => {
12 e.preventDefault();
13 zone.classList.remove('dragover');
14 });
15});
16
17// Handle dropped files
18zone.addEventListener('drop', (e) => {
19 const files = e.dataTransfer.files;
20 handleFiles(files);
21});
Multi-File with Previews

A multi-file upload zone that shows thumbnails for images and file type icons for other files. Each file displays its name, size, a progress bar, and a remove button. The footer shows the total file count and combined size.

multi-preview
Live
untitled.html
HTML
1<div class="mp-upload">
2 <div class="mp-zone" id="mp-zone">
3 <input type="file" class="file-input" id="mp-input" multiple/>
4 <label for="mp-input" class="mp-label">
5 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="24" height="24">
6 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
7 <polyline points="17 8 12 3 7 8"/>
8 <line x1="12" y1="3" x2="12" y2="15"/>
9 </svg>
10 <span>
11 Add files or drop here
12 </span>
13 </label>
14 </div>
15 <div class="mp-list">
16 <div class="mp-item">
17 <div class="mp-thumb img" style="background:linear-gradient(135deg,#1a1a2e,#2d1b69)">
18 <span>
19 IMG
20 </span>
21 </div>
22 <div class="mp-info">
23 <span class="mp-name">
24 hero-banner.png
25 </span>
26 <span class="mp-size">
27 2.4 MB
28 </span>
29 </div>
30 <div class="mp-progress">
31 <div class="mp-bar" style="width:100%">
32 </div>
33 </div>
34 <button class="mp-remove" aria-label="Remove file">
35
36 </button>
37 </div>
38 <div class="mp-item">
39 <div class="mp-thumb img" style="background:linear-gradient(135deg,#0f2027,#203a43)">
40 <span>
41 IMG
42 </span>
43 </div>
44 <div class="mp-info">
45 <span class="mp-name">
46 team-photo.jpg
47 </span>
48 <span class="mp-size">
49 1.8 MB
50 </span>
51 </div>
52 <div class="mp-progress">
53 <div class="mp-bar" style="width:100%">
54 </div>
55 </div>
56 <button class="mp-remove" aria-label="Remove file">
57
58 </button>
59 </div>
60 <div class="mp-item">
61 <div class="mp-thumb doc">
62 <span>
63 PDF
64 </span>
65 </div>
66 <div class="mp-info">
67 <span class="mp-name">
68 project-spec.pdf
69 </span>
70 <span class="mp-size">
71 840 KB
72 </span>
73 </div>
74 <div class="mp-progress">
75 <div class="mp-bar" style="width:65%">
76 </div>
77 </div>
78 <button class="mp-remove" aria-label="Remove file">
79
80 </button>
81 </div>
82 </div>
83 <div class="mp-footer">
84 <span class="mp-count">
85 3 files selected
86 </span>
87 <span class="mp-total">
88 Total: 5.0 MB
89 </span>
90 </div>
91</div>
preview
Upload Progress

Files with animated progress bars showing upload status. Each bar displays the percentage complete with a shimmer animation during upload. Completed files show a checkmark with a green background, while queued files display a clock icon.

upload-progress
Live
untitled.html
HTML
1<div class="prog-upload">
2 <div class="pu-item done">
3 <div class="pu-icon done">
4 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="14" height="14">
5 <polyline points="20 6 9 17 4 12"/>
6 </svg>
7 </div>
8 <div class="pu-details">
9 <div class="pu-row">
10 <span class="pu-name">
11 design-system.zip
12 </span>
13 <span class="pu-status done">
14 Complete
15 </span>
16 </div>
17 <div class="pu-bar-wrap">
18 <div class="pu-bar done" style="width:100%">
19 </div>
20 </div>
21 <span class="pu-meta">
22 4.2 MB · 12s
23 </span>
24 </div>
25 </div>
26 <div class="pu-item active">
27 <div class="pu-icon active">
28 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
29 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
30 <polyline points="17 8 12 3 7 8"/>
31 <line x1="12" y1="3" x2="12" y2="15"/>
32 </svg>
33 </div>
34 <div class="pu-details">
35 <div class="pu-row">
36 <span class="pu-name">
37 assets-bundle.tar.gz
38 </span>
39 <span class="pu-status active">
40 72%
41 </span>
42 </div>
43 <div class="pu-bar-wrap">
44 <div class="pu-bar active" style="width:72%">
45 </div>
46 </div>
47 <span class="pu-meta">
48 18.6 MB · Uploading...
49 </span>
50 </div>
51 </div>
52 <div class="pu-item queued">
53 <div class="pu-icon">
54 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
55 <circle cx="12" cy="12" r="10"/>
56 <polyline points="12 6 12 12 16 14"/>
57 </svg>
58 </div>
59 <div class="pu-details">
60 <div class="pu-row">
61 <span class="pu-name">
62 video-recording.mp4
63 </span>
64 <span class="pu-status">
65 Queued
66 </span>
67 </div>
68 <div class="pu-bar-wrap">
69 <div class="pu-bar" style="width:0%">
70 </div>
71 </div>
72 <span class="pu-meta">
73 142 MB · Waiting
74 </span>
75 </div>
76 </div>
77</div>
preview
File Validation

Client-side validation that checks file type, size, and count before upload. Invalid files show inline error messages with the specific rejection reason, while valid files proceed to the upload queue with a green checkmark.

validation
Live
untitled.html
HTML
1<div class="valid-upload">
2 <div class="vu-zone" id="vu-zone">
3 <input type="file" class="file-input" id="vu-input" accept=".jpg,.jpeg,.png,.gif,.pdf,.docx" multiple/>
4 <label for="vu-input" class="vu-label">
5 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="28" height="28">
6 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
7 <polyline points="17 8 12 3 7 8"/>
8 <line x1="12" y1="3" x2="12" y2="15"/>
9 </svg>
10 <span>
11 Drop files here
12 </span>
13 <span class="vu-hint">
14 JPG, PNG, GIF, PDF, DOCX · Max 5MB · Max 5 files
15 </span>
16 </label>
17 </div>
18 <div class="vu-list">
19 <div class="vu-item valid">
20 <div class="vi-icon valid">
21 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="12" height="12">
22 <polyline points="20 6 9 17 4 12"/>
23 </svg>
24 </div>
25 <div class="vi-info">
26 <span class="vi-name">
27 photo.jpg
28 </span>
29 <span class="vi-detail">
30 1.2 MB · Image
31 </span>
32 </div>
33 </div>
34 <div class="vu-item invalid">
35 <div class="vi-icon invalid">
36
37 </div>
38 <div class="vi-info">
39 <span class="vi-name">
40 video.mp4
41 </span>
42 <span class="vi-detail invalid">
43 File type not allowed · MP4
44 </span>
45 </div>
46 </div>
47 <div class="vu-item invalid">
48 <div class="vi-icon invalid">
49
50 </div>
51 <div class="vi-info">
52 <span class="vi-name">
53 large-file.zip
54 </span>
55 <span class="vi-detail invalid">
56 Exceeds 5MB limit · 24 MB
57 </span>
58 </div>
59 </div>
60 </div>
61</div>
preview
Avatar Upload

A circular avatar upload with image preview. Clicking the avatar or button opens the file picker filtered to images. The selected image appears as a circular preview using FileReader.readAsDataURL().

avatar-upload
Live
untitled.html
HTML
1<div class="avatar-upload">
2 <div class="av-wrap">
3 <div class="av-preview" id="av-preview">
4 <div class="av-placeholder">
5 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="28" height="28">
6 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
7 <circle cx="12" cy="7" r="4"/>
8 </svg>
9 </div>
10 </div>
11 <input type="file" class="file-input" id="av-input" accept="image/*"/>
12 <label for="av-input" class="av-btn">
13 Change Photo
14 </label>
15 <span class="av-hint">
16 JPG or PNG · Max 2MB
17 </span>
18 </div>
19</div>
preview
Image Preview Grid

A grid of image thumbnails generated from selected files using URL.createObjectURL(). Each thumbnail has an overlay with file name and a remove button. Click any thumbnail to see a larger preview.

image-grid
Live
untitled.html
HTML
1<div class="img-grid-demo">
2 <div class="igd-grid">
3 <div class="igd-thumb">
4 <div class="igd-img" style="background:linear-gradient(135deg,#1a1a2e,#2d1b69)">
5 </div>
6 <div class="igd-overlay">
7 <span class="igd-name">
8 design-v1.png
9 </span>
10 <button class="igd-x" aria-label="Remove">
11
12 </button>
13 </div>
14 </div>
15 <div class="igd-thumb">
16 <div class="igd-img" style="background:linear-gradient(135deg,#0f2027,#203a43)">
17 </div>
18 <div class="igd-overlay">
19 <span class="igd-name">
20 hero-banner.jpg
21 </span>
22 <button class="igd-x" aria-label="Remove">
23
24 </button>
25 </div>
26 </div>
27 <div class="igd-thumb">
28 <div class="igd-img" style="background:linear-gradient(135deg,#0c2340,#1e5a8a)">
29 </div>
30 <div class="igd-overlay">
31 <span class="igd-name">
32 team-photo.png
33 </span>
34 <button class="igd-x" aria-label="Remove">
35
36 </button>
37 </div>
38 </div>
39 <div class="igd-thumb">
40 <div class="igd-img" style="background:linear-gradient(135deg,#2d1810,#5a3a28)">
41 </div>
42 <div class="igd-overlay">
43 <span class="igd-name">
44 screenshot.png
45 </span>
46 <button class="igd-x" aria-label="Remove">
47
48 </button>
49 </div>
50 </div>
51 </div>
52</div>
preview
HTML Structure

The upload zone wraps a hidden <input type="file"> with a visible <label> for click-to-browse. Drag-and-drop events are handled on the container element. Always include aria-live="polite" on the file list.

upload-structure.html
HTML
1<div class="upload-zone" role="button" tabindex="0">
2 <input type="file" class="sr-only" id="file-upload"
3 multiple accept=".jpg,.png,.pdf" />
4
5 <label for="file-upload" class="upload-label">
6 <svg><!-- upload icon --></svg>
7 <span>Drop files here or click to browse</span>
8 <small>JPG, PNG, PDF · Max 10MB</small>
9 </label>
10</div>
11
12<!-- File list after selection -->
13<ul class="file-list" aria-live="polite">
14 <li class="file-item">
15 <span class="file-icon">📄</span>
16 <span class="file-name">document.pdf</span>
17 <span class="file-size">1.2 MB</span>
18 <button aria-label="Remove document.pdf">✕</button>
19 </li>
20</ul>
Best Practices

info

Always validate files client-side before uploading \u2014 check type, size, and count. Server-side validation is still required, but client-side checks provide instant feedback and reduce unnecessary network traffic.

warning

Set the accept attribute on the file input to filter the file picker, but never rely on it alone for validation \u2014 users can bypass it. Always validate on the server.

best practice

Use aria-live="polite" on the file list container so screen readers announce new files and removals without interrupting the current task.
  • Show file size and type icons in the file list so users can quickly verify their selections before uploading.
  • Support keyboard navigation: Enter/Space to open file picker, Delete to remove selected files, Tab to navigate the list.
  • For drag-and-drop, highlight the drop zone on dragenter and reset on dragleave/drop.
  • Show upload progress for files over 1MB \u2014 users need feedback for large uploads. Use a progress bar with percentage and file size.
  • Generate image previews using URL.createObjectURL() or FileReader.readAsDataURL() before uploading.