Multi-File Upload Queue — Concurrency-Limited Upload Loader

Multi-File Upload Queue · Loaders · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Concurrency-limited queue
Only MAX_CONCURRENT files upload at once; the rest wait their turn.
Auto-advancing slots
fillQueue() pulls the next waiting file in the moment a slot frees up.
Independent per-file timers
Each active upload runs its own randomised-speed interval.
Realistic staggered finishes
Files complete at genuinely different, unscripted times.
Real failure and retry
One file errors mid-upload and can be retried back into the queue.
Live true-average summary
The top bar recomputes the real average progress every tick.
Status breakdown
Active, waiting, done, and failed counts update live in the meta line.
Backend-ready structure
Swap the simulated interval for real XHR upload.onprogress with no other changes.

About this UI Snippet

Multi-File Upload Queue — Limited-Concurrency Uploads with Retry

Screenshot of the Multi-File Upload Queue snippet rendered live

Real upload managers rarely fire every file at once — bandwidth and browser connection limits mean only a handful upload concurrently while the rest wait their turn. This snippet models that honestly: six files queue up, only two upload at a time, each running file advances on its own randomised interval so they finish at different moments, one file genuinely fails partway through with a retry action, and a live aggregate bar and status line summarise the whole queue — all in plain HTML, CSS, and vanilla JavaScript, no drag-and-drop chrome, focused purely on the queue mechanics.

A concurrency-limited queue, not a free-for-all

fillQueue() is the core of the snippet: it counts how many files are currently active and starts new ones from the waiting pool only until MAX_CONCURRENT (2) is reached. Every time a file finishes — successfully or with an error — fillQueue() runs again, pulling the next waiting file into an open slot. This is the same limited-concurrency pattern real upload clients use to avoid saturating a connection, and it's a meaningfully different mechanic from a simple "start everything simultaneously" uploader.

Independent, realistically staggered progress

Each active file gets its own setInterval with a randomised speed, so two files running side by side visibly finish at different times — one might complete in three seconds, another in eight. Nothing is synchronised or faked to look tidy; the raw randomness is what makes the queue feel like real network activity rather than a scripted animation.

A genuine failure and retry

One file (ERROR_INDEX) is scripted to fail once it passes 40% progress on its first attempt, switching to a red error row with an inline Retry button. Clicking Retry calls startFile again for that index, which restarts its progress from zero and re-enters the active pool — this time it's allowed to succeed, demonstrating a real state transition from error back to active rather than a decorative error icon that never resolves.

A live, honest aggregate

The top summary bar isn't a rough estimate — updateSummary() recomputes the true average of every file's current progress (treating a failed file as 0% until it's retried) on every tick, plus a live breakdown of how many files are active, waiting, done, or failed. This mirrors exactly what a user needs to know at a glance: how far along is the whole batch, and is anything stuck.

Wiring it to a real backend

Replace startFile's simulated interval with a real XMLHttpRequest (or fetch with a readable stream) per file, updating s.progress from upload.onprogress's loaded/total, and call fillQueue() from that request's load/error handlers instead of the simulated completion. The concurrency-limiting, retry, and summary logic all stay exactly the same. Pair it with the drag-and-drop upload progress pattern for the initial file-picking step, or a loading overlay while the queue completes.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how fillQueue() enforces MAX_CONCURRENT by counting active files and only pulling from the waiting pool when a slot is free, and why it needs to be called again after both a successful completion and a failure for the queue to keep draining correctly. It's worth an efficiency check too: ask whether recomputing the full average in updateSummary() on every single tick of every active file matters at six files versus sixty, and how you'd batch those updates if it did. For extending it, ask for a version wired to real XMLHttpRequest uploads with genuine upload.onprogress events, a configurable MAX_CONCURRENT exposed as a UI control, or a pause/cancel action per file that correctly frees its slot for the next waiting item. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "multi-file upload queue" in plain HTML, CSS, and JavaScript that enforces a maximum number of concurrent uploads — no drag-and-drop UI needed, focus purely on the queue mechanics.

Requirements:
- A list of several files (name, size, icon) each rendered as a row with its own progress bar and a status label that can read Waiting, an active percentage, Done, or Failed.
- A JavaScript state array tracking each file's status (waiting, active, done, or error) and progress percentage, with a constant limiting how many files may be in the active status simultaneously (e.g. 2).
- A queue-filling function that counts currently active files and starts new ones from the waiting pool only until the concurrency limit is reached, and must be re-invoked every time any file finishes or fails, so a freed slot is immediately backfilled by the next waiting file — files must not all start simultaneously regardless of the limit.
- Each active file must progress independently via its own interval timer with a randomized speed, so that two files running concurrently visibly complete at different, unscripted times rather than in lockstep.
- Exactly one file must be scripted to fail partway through its first upload attempt (after passing some progress threshold), switching its row to a distinct error state with an inline Retry button; clicking Retry must restart that file's upload from zero and allow it to re-enter the concurrency-limited active pool, succeeding on the second attempt.
- A top-level summary section must show a live aggregate progress bar and percentage computed as the true running average of every file's current individual progress (not a separate faked value), plus a text line breaking down how many files are currently active, waiting, done, and failed, all updating in real time as the queue drains.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSSix files render, two begin uploading immediately, the rest sit "Waiting".
  2. 2
    Watch the queue advanceAs each active file finishes, the next waiting file automatically starts.
  3. 3
    See the scripted failureOne file fails partway through and shows a Retry button.
  4. 4
    Click RetryThat file restarts from zero and succeeds on its second attempt.
  5. 5
    Watch the summary barThe top bar and meta line track the true live average and counts.
  6. 6
    Run it againOnce every file settles, click "Run queue again" to reset and replay.

Real-world uses

Common Use Cases

Media and asset upload managers
Batch uploads of large files where bandwidth limits concurrent transfers.
Backup and sync clients
A web dashboard for a sync tool showing queued vs. active file transfers.
Bulk document ingestion
Compare with upload progress for the picker step feeding this queue.
CMS and DAM bulk imports
Import many assets while capping concurrent requests to the media API.
Data pipeline job queues
Adapt the same concurrency-limited pattern for background job processing.
Any rate-limited API upload
Respect a backend's concurrent-request limit while keeping users informed.

Got questions?

Frequently Asked Questions

fillQueue() counts files currently in the active status and starts more from the waiting pool only until MAX_CONCURRENT is reached. It's called again every time a file finishes or errors, so a freed slot is immediately backfilled from the queue — the same mechanic real upload managers use to avoid overwhelming a connection.

Each active file runs its own setInterval with a randomised per-tick speed, so two files started at the same moment naturally diverge — one may finish well before the other. Nothing is synchronised on purpose; the unscripted variance is what makes the queue read as real network activity.

One file is scripted to fail once past 40% progress on its first attempt, switching to an error row with a Retry button. Clicking Retry calls startFile again for that index, resetting its progress to zero and re-entering the active pool exactly like a fresh upload — this time it's allowed to complete, so the failure-to-success transition is real, not decorative.

It's a real average. updateSummary() sums every file's current progress (a failed file counts as 0% until retried) and divides by the total file count on every tick, so the top bar and percentage always reflect the true, live state of every row — not a smoothed or faked approximation.

Replace startFile's setInterval with a real XMLHttpRequest per file: update s.progress from xhr.upload.onprogress's loaded/total, and call fillQueue() from the request's load and error handlers instead of the simulated completion. Keep MAX_CONCURRENT, the retry button, and updateSummary unchanged — they operate purely on the state array regardless of what drives it.