Drag & Drop File Upload Demo - Online With Preview
Drop files onto a zone and see a preview with name, size, and type. Copy the JavaScript pattern for your site.
UD5 Toolkit
Real-time monitoring of XHR & Fetch upload progress with live speed, ETA, and detailed logs
Drag & drop a file here
Tap to select or drop file
or click to browse files
Max 50MB for real upload Β· Any size for simulation
function xhrUploadWithProgress(url, file, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
// Track upload progress via xhr.upload
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
onProgress(e.loaded, e.total);
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) resolve(xhr);
else reject(new Error(`HTTP ${xhr.status}`));
});
xhr.addEventListener('error', () => reject(new Error('Network error')));
xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));
xhr.open('POST', url);
xhr.send(file); // Native progress events fire automatically
});
}
// Fetch API has NO native upload progress events.
// We wrap the file stream with ReadableStream to track bytes sent.
async function fetchUploadWithProgress(url, file, onProgress, signal) {
const fileStream = file.stream();
const reader = fileStream.getReader();
let uploaded = 0;
const trackingStream = new ReadableStream({
async pull(controller) {
const { done, value } = await reader.read();
if (done) { controller.close(); return; }
uploaded += value.byteLength;
onProgress(uploaded, file.size);
controller.enqueue(value);
}
});
const response = await fetch(url, {
method: 'POST',
body: trackingStream,
duplex: 'half', // Required for streaming body
signal,
headers: {
'Content-Type': file.type || 'application/octet-stream',
'Content-Length': String(file.size),
}
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response;
}
progress events on xhr.upload, fetch treats the request body as a stream. To track upload progress, you need to wrap the body stream with a ReadableStream and count bytes as they are read β this is the modern, composable approach. Browser vendors opted for stream-based APIs over event-based ones for greater flexibility.
duplex: 'half' and why is it required?
duplex: 'half' tells the browser that the request body is a half-duplex stream β data flows in one direction (client β server). This is required when using a ReadableStream as the fetch body. Without it, the browser will throw a TypeError. This option was introduced to support streaming request bodies while maintaining backward compatibility. It's supported in Chrome 105+, Firefox 120+, Safari 17+, and Edge 105+.
xhr.upload.onprogress event is supported in all browsers and gives accurate, real-time progress data. Fetch with ReadableStream wrapping is the modern equivalent but requires newer browser APIs. For production applications targeting all browsers, XHR remains the safer choice. For modern SPAs, the Fetch + ReadableStream pattern works well with proper feature detection.
xhr.abort() β this triggers the abort event. For Fetch, create an AbortController, pass its signal to the fetch options, and call controller.abort() to cancel. When using ReadableStream for upload tracking, the stream's pull() method will stop being called after cancellation. Both methods cleanly terminate the upload.
speed = deltaBytes / deltaTime. This provides a smooth, responsive speed reading that adapts to network fluctuations without excessive jitter. ETA is then derived as remainingBytes / currentSpeed.
pull() is called once, reads the whole file, and progress jumps from 0% to 100% instantly. This is expected behavior. For meaningful progress tracking, files should be at least a few hundred KB. Use the simulation mode in this tool to see granular progress with any file size.
xhr.upload.onprogress. In Fetch, wrap the request body stream. Download progress tracks data received from server to client (response body). In XHR, use xhr.onprogress. In Fetch, read response.body.getReader() and count bytes. These are separate concerns β don't confuse them!
FormData for multipart uploads?
FormData, the browser constructs the multipart body internally. You cannot easily wrap it with a ReadableStream for progress tracking. The best approach is to either: (a) use XHR with FormData (native progress events work), or (b) construct the multipart body manually as a Blob or stream. For simple file uploads, sending the raw file with Content-Type is simpler and fully trackable.
Drop files onto a zone and see a preview with name, size, and type. Copy the JavaScript pattern for your site.
Load an image and see the exact events fired (load, error, abort). Monitor progress. Dev helper.
Start a fetch request and cancel it with AbortController. See how to implement request cancellation. Interactive.
Generate a printable pace band for ultra distances with mile/km splits and cumulative time. Adjust for terrain.
Enter a URL and see its favicon at all standard sizes. Check if it's properly defined. SEO basic check.
Enter a URL and get a list of all images that fail to load (404, CORS). A simple asset audit tool. Runs in your browser.
See how your browser renders emojis from different Unicode versions. Detect gaps in your system's emoji font support.
Enter an emoji or browse to see which Unicode version introduced it and whether older OS versions might fail.
Pick a color palette and the tool shows sample images that match it. Great for moodboard and brand inspiration. Static dataset.
View every standard emoji in a searchable grid. Hover to see a larger preview. Click to copy. Updated with Unicode 16.
Paste HTML code and extract all comments or remove them completely. See a list of comments found. Clean your markup.
Paste your email subject and body. The tool highlights common spam trigger words and gives a risk score. Improve inbox placement.
Convert between emoji characters and their shortcode representations (e.g., :tada: to π). Supports Slack and GitHub styles.
Paste a list of words or lines and convert all to UPPERCASE or lowercase at once. Also supports title case.
Select a heading font and get recommended body font pairings. Live preview with sample text. Copy the CSS import links.
Quiz yourself on element symbols, names, and atomic numbers. Multiple modes: element to symbol, symbol to element. Score tracking. Great for chemistry students.
Convert running shoe sizes between US (men/women), UK, EU, and centimeters. Also includes brand-specific fit notes (general guide).
Transform regular text into the mocking SpongeBobβstyle alternating case. Copy and paste for hilarious effect.
Use the new `commandfor` and `command` attributes to invoke actions on other elements without JavaScript. See the spec live.
Learn how to let content extend a few pixels beyond a clip boundary with overflowβclipβmargin. Interactive playground.
Configure how your PWA launches: focus existing or create new. Test with the launch_handler manifest field.
Set up a Shared Worker that shares state across multiple browser tabs. Counter and messaging demo. Advanced web.
See how WeakMap and WeakSet work. Add objects as keys and watch references. Understand memoryβefficient collections.
Toggle imageβrendering: auto, pixelated, crispβedges on a scaled image. Essential for pixel art display.
Press Ctrl+V to paste an image from your clipboard into the page. See dimensions and download. Simple utility.
Calculate large Fibonacci numbers in a Web Worker. See the UI remain responsive. Copy the pattern for your app.
See how grid-auto-flow: row vs column changes item placement. Add and remove items to understand the algorithm. Visual.
Demonstrate how to add custom headers and POSTβlike functionality to EventSource using a polyfill. Code and example.
Toggle between light and dark mode for a demo page. See how to use the media query. Copy the pattern.
See the roving tabindex pattern in action. Use arrow keys to navigate a list. Copy the accessible JavaScript pattern.