# Upload con progresso reale: cosa misurare

Il progresso di un upload arriva dai byte dichiarati dal browser, non da un timer: onUploadProgress misura l'invio, non la fine del lavoro.

> Pagina: https://ui.eneawork.it/blog/upload-form-progress
> Markdown: https://ui.eneawork.it/blog/upload-form-progress.md

*2026-11-01*

Il progresso di un upload si legge dai byte che il browser dichiara di aver inviato, con l'evento upload.onprogress di XMLHttpRequest: un timer che avanza non misura nulla.

Il catalogo EneUI conta 111.985 componenti, 1.556 blocchi e 12.808 icone da 1.472 librerie, misurati da /api/stats il 1 novembre 2026. La cifra viene da una query all'endpoint, non da una stima: conta i record pubblicati, non la qualità dei componenti.

## Perché fetch non lo espone

Con fetch il corpo della richiesta è uno stream opaco e non esiste un evento di avanzamento dell'invio: il download si legge a pezzi, l'upload no. Servono XMLHttpRequest o una libreria che lo incapsula, come axios, distribuito con licenza MIT.

## Il codice che misura

function carica(file, onProgress) { const body = new FormData(); body.append("file", file); return axios.post("/api/upload", body, { onUploadProgress: (e) => onProgress(Math.round((e.loaded / e.total) * 100)), }); }

La percentuale è loaded diviso total: misura l'invio dei byte, non l'elaborazione del file sul server.

## Le trappole

## Installazione

npm i axios

L'installazione aggiunge una sola dipendenza MIT; onUploadProgress funziona nei browser che espongono XMLHttpRequest, quindi non nei test con jsdom.

## Record vivi nel catalogo

Tre componenti di caricamento file: file-upload-card, file-upload-2 e avatar-uploader. Per sfogliare per categoria parti da 111.985 componenti.

## La limitazione

La barra misura l'invio, non l'esito: se il server accoda il file, l'utente vede 100% e attesa. Per un progresso onesto serve una seconda fase, con un id di job da interrogare.

## Dove continuare

La route handler per i file è in upload con Next.js; il confronto fra i due schemi di campo è in form controllati e non; la scelta di framework è in Vue 3 vs React.

## FAQ

### Perché la barra arriva a 100% e non finisce?

Perché misura l'invio, non l'elaborazione. Mostra uno stato separato per l'attesa della risposta.

### fetch supporta il progresso di upload?

No, non per il corpo inviato. Con fetch leggi il download a pezzi, non i byte in uscita.

### Come testo la barra?

Simuli un evento di progresso sul finto XMLHttpRequest: jsdom non invia byte e non emette eventi veri.

### Serve axios per forza?

No. XMLHttpRequest basta; axios incapsula la stessa API e aggiunge una dipendenza MIT.

### Il tempo residuo è affidabile?

No. I byte sono un dato reale, la stima del tempo dipende da una velocità che cambia.
