Show the file before upload
Render the picked file from its local copy now; upload behind it.
Guillermo Rauch7 Principles of Rich Web ApplicationsWe can capture the user's intent to upload through a variety of means: drag and drop, paste, file picker. Then, thanks to new HTML5 APIs we can display content as if it had been uploaded.
Guillermo Rauch@rauchgThoughts on rendering the image optimistically from the local reference? Rn it’s causing a layout shift
Guillermo Rauch@rauchgI'm so impressed with @telegram's engineering … ▪️ Media uploads are fast and… convey progress
Demo 1 renders the local file with no shift, as his 2025 post asks; demo 2 is Telegram’s.
1 · Send a photo in chat
Send a sample, or pick, drop or paste your own image.
or drop / paste onto a chat
Wait for the uploadBad
Visible after —Shift —
Local copy, sizedGood
Visible after —Shift —
async function send(file) {
const src = URL.createObjectURL(file);
const { width, height } = await createImageBitmap(file);
const msg = addMessage({ src, width, height, progress: 0 }); // aspect-ratio: width / height
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => msg.setProgress(e.loaded / e.total);
xhr.onload = () => { msg.sent(); URL.revokeObjectURL(src); };
xhr.onerror = () => msg.failed({ retry: () => send(file) }); // keep the file
xhr.open("POST", "/api/upload");
xhr.send(file);
}
2 · Progress you can trust
Send an album, then stall the network halfway.
One spinnerBad
idle
Bytes, per photoGood
idle
Why it matters, pros and cons
Why it matters
The file is already on the device. Show it now; the upload is bookkeeping.
Pros
- The photo appears the frame it’s picked, not seconds later.
- Width and height come from the file, so nothing shifts.
- Progress on the real image says moving, or stuck.
Cons
- Object URLs hold memory until you revoke them.
- Failure must keep the file and offer Retry, not drop it.
- Server crops or filters can differ from the local preview.