Component
Upload Progress
Multi-file upload list where each file shows an animated progress bar and percentage, then transitions to a green success check when complete. Files can be removed mid-upload. Call addFile(name) to push files programmatically.
- Component
- Tailwind
- HTML
- MIT
What it does
Capabilities
- Per-file progress bars
- Percentage labels
- Success state transition
- Remove mid-upload
- Programmatic addFile()
- Role=progressbar
- Aria-valuenow
- Tabular-nums
Preview
Live component
9:41 100%
devsnips.site/library/tailwind/components/progress/upload-progress/ fluid · no fixed width 100%
Install
Add to your project
npx devsnips add Tailwind/Components/Progress/upload-progress Tailwind/Components/Progress/upload-progress <!--
Snippet Name: Upload Progress
Description: Multi-file upload list where each file shows an animated progress bar, then transitions to a success check. Files can be removed before completing.
Author: DevSnips Contributors
Usage Example: Call addFile(name) to push a file; the list simulates upload progress and flips to success.
-->
<div class="w-full max-w-md space-y-3" data-upload-list></div>
<!-- Demo trigger -->
<label class="inline-flex cursor-pointer items-center gap-2 rounded-xl bg-blue-600 px-5 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3 3-3"/></svg>
Upload files
<input type="file" multiple class="sr-only" />
</label>
<script>
(function () {
const list = document.currentScript.closest('[data-upload-list]') || document.querySelector('[data-upload-list]');
if (!list) return;
function el(tag, cls, html) {
const e = document.createElement(tag);
if (cls) e.className = cls;
if (html !== undefined) e.innerHTML = html;
return e;
}
function addFile(name) {
const item = el('div', 'upload-item flex items-center gap-3 rounded-xl border border-gray-200 bg-white p-3 shadow-sm');
item.innerHTML = `
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-blue-50 text-blue-600">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</span>
<div class="min-w-0 flex-1">
<div class="mb-1.5 flex items-center justify-between gap-2">
<p class="upload-name truncate text-sm font-medium text-gray-900"></p>
<span class="upload-pct text-xs font-semibold tabular-nums text-gray-500">0%</span>
</div>
<div class="relative h-1.5 w-full overflow-hidden rounded-full bg-gray-100" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="upload-fill absolute inset-y-0 left-0 w-0 rounded-full bg-gradient-to-r from-blue-500 to-blue-600 transition-[width] duration-300 ease-out"></div>
</div>
</div>
<button type="button" class="upload-remove shrink-0 rounded-md p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-400" aria-label="Remove file">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
</button>`;
item.querySelector('.upload-name').textContent = name;
list.appendChild(item);
const fill = item.querySelector('.upload-fill');
const pct = item.querySelector('.upload-pct');
const bar = item.querySelector('[role="progressbar"]');
const iconWrap = item.querySelector('.flex.h-9');
const removeBtn = item.querySelector('.upload-remove');
let value = 0;
function remove() {
item.classList.add('opacity-0', 'translate-x-2', 'transition', 'duration-300');
setTimeout(() => item.remove(), 300);
}
removeBtn.addEventListener('click', remove);
const timer = setInterval(() => {
value = Math.min(100, value + Math.random() * 22);
fill.style.width = value + '%';
pct.textContent = Math.round(value) + '%';
bar.setAttribute('aria-valuenow', String(Math.round(value)));
if (value >= 100) {
clearInterval(timer);
setTimeout(() => {
// flip to success
fill.classList.remove('from-blue-500', 'to-blue-600');
fill.classList.add('from-green-500', 'to-green-600');
iconWrap.className = 'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-green-50 text-green-600';
iconWrap.innerHTML = '<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>';
pct.textContent = 'Done';
pct.classList.remove('text-gray-500');
pct.classList.add('text-green-600');
}, 200);
}
}, 500);
}
window.addFile = addFile;
// Demo: wire the file input + a couple of seed files
const input = document.querySelector('input[type="file"]');
if (input) {
input.addEventListener('change', (e) => {
Array.from(e.target.files).forEach((f) => addFile(f.name));
e.target.value = '';
});
}
addFile('design-system.fig');
addFile('hero-photo.png');
})();
</script> # Upload Progress
Upload progress indicator.
## Usage
```bash
npx devsnips add Tailwind/Components/Progress/upload-progress
```
Copy from `code.html`.
## File Structure
`code.html` · `preview.html` · `metadata.json` · `README.md` 97 lines UTF-8 · LF · Spaces: 2
DevSnips AI ready Claude · Cursor · Windsurf
Agent skill & Model Context Protocol
Pass this component straight into your AI workflow. DevSnips exposes each resource as a structured, addressable registry entry, so your agent installs the real files instead of approximating them.
devsnips://components/progress/upload-progress I want to use this DevSnips resource in production: Page: https://devsnips.site/library/tailwind/components/progress/upload-progress/ Install: npx devsnips add Tailwind/Components/Progress/upload-progress This is the Upload Progress — Multi-file upload list where each file shows an animated progress bar and percentage, then transitions to a green success check when complete. Files can be removed mid-upload. Call addFile(name) to push files programmatically. Use this DevSnips resource as the starting point for my project. Adapt it to my project's stack and conventions, fix any issues, and make it production-ready while preserving its intended design and behavior.
Paste into ChatGPT, Claude or your agent — it already carries the page URL and the exact install path.
Resource URI
devsnips://components/progress/upload-progress