Skip to content
EspañolEnglish
All components (204)

Upload with progress

Management · Uploader · .amset-uploader

What it is and when to use it

Uploading files for real, as soon as they are chosen: the FileUpload zone to drop or choose them and, below, one row per file with its progress bar, the reason if it fails, a button to retry and another to remove it, which cancels the upload. It is FileUpload + FileList + useFileUploads, already mounted together.

When to use it

  • Attachments that are uploaded when chosen: invoices, receipts, the photos of a product.
  • http uploads over HTTP, with a multipart form: action (the address, or a function that works it out for each file), method (POST by default, or PUT), headers, data (extra fields that travel with the file), fieldName (file by default) and withCredentials. It uses XMLHttpRequest and not fetch because fetch does not say how much has gone out, and without that the bar would not move.
  • customRequest uploads wherever whoever uses it says (a signed URL, a storage SDK) and wins over http: it receives { file, onProgress, signal }, calls onProgress with the percentage and returns a promise with what the server answers. It has to respect signal, which is aborted when the file is removed.
  • concurrency is how many upload at once (3 by default): the rest wait their turn, and one that fails does not bring down the others.
  • onChange reports the whole list on every change; onUploaded and onFailed, each file that finishes or fails (what the server answered arrives in its response); onReject, what does not pass the type, size or how-many-fit filter.
  • The zone is that of FileUpload, with the same props: label, hint, error, acceptedTypes, maxSizeMb, multiple, maxCount, directory, size and disabled. Without multiple, choosing another file replaces the previous one and cancels its upload.
  • The type and size filter is a convenience: the server has to check it again.

When not to

  • Choosing files that travel with the form when it is sent, without uploading them first: FileUpload.
  • Uploading when "Send" is pressed and not on choosing: useFileUploads with autoStart: false and its start(), with FileUpload (hideList) and FileList rendered on your own.
  • Showing attachments that are already uploaded: FileList.

Keyboard: that of FileUpload: Tab reaches the zone and its button (Enter or Space open the system picker; with focus on the zone files can be pasted), and then the buttons of each row: "Retry" if it failed and "Remove". On removing one, focus moves to the list, which says how many are left; on removing the last one, the list disappears and focus goes back to the choose button, with the notice "factura.pdf removed. None left".

Accessibility: the list is a region named after the zone ("Uploads for Invoices"). While a file uploads, its row carries a progress bar with a name ("Uploading factura.pdf"); queued, it carries no bar, because "0 %" would say it had already started. Each change of status is announced once in a status region —"factura.pdf uploaded", "Could not upload falla.pdf: 413 Payload Too Large"—, and several that finish at once are announced together; the progress is seen in the bar, without being announced a hundred times. The buttons say which file they are for ("Remove factura.pdf"). The network, timeout and status errors written by http come out in the language of http.locale (Spanish, by default).

Fine-tuning: --amset-uploader-gap, the gap between the zone and the list; the zone, with the --amset-upload-* of FileUpload, and the list, with the --amset-files-* of FileList.

What it looks like

Invoices
Drag the files here

PDF up to 10 MB. Try a file called falla.pdf.

<Uploader
  label="Invoices"
  hint="PDF up to 10 MB"
  acceptedTypes={['application/pdf']}
  maxSizeMb={10}
  multiple
  maxCount={5}
  http={{ action: '/api/facturas' }}
/>

Props

Import it with import { Uploader } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
labelrequiredstringWhat is being uploaded. It is the name of the drop zone and of the list of chosen items.
acceptedTypesreadonly string[]MIME types or extensions: `['application/pdf', '.csv']`.
classNamestringMerged with the library classes; it does not replace them.
concurrencynumberHow many upload at once. By default, 3: more saturates the connection and all of them finish later.
customRequestUploadRequestYour own upload (an SDK, a signed URL...). It wins over `http`.
directorybooleanChoose a whole FOLDER instead of separate files. Dropping a folder onto it does not traverse it.
disabledboolean—
errorstring—
hiddenLabelbooleanThe label for the screen reader only. The drop zone already carries its own name, so it can be hidden from view without losing anything.
hintstringHelp text: which formats and what size are accepted.
httpHttpUploadConfigUpload over HTTP. Without it `customRequest` is needed.
inputRefRef<HTMLInputElement>The real `<input type="file">` (it is hidden), to open the picker from your own button with `inputRef.current?.click()` (AMS-735).
maxCountnumberHow many files fit at most. The excess ones are rejected with their reason. Without this, no limit.
maxSizeMbnumberMaximum size per file, in megabytes.
multipleboolean—
onChange(entries: readonly UploadEntry[]) => voidNotifies of each change in the list: a new file, a progress step, a failure, one that finishes.
onFailed(entry: UploadEntry) => voidA file has failed. It does not include those cancelled by removing them.
onReject(rejected: readonly RejectedFile[]) => voidCalled with those that did not pass the filter (type, size, how many fit).
onUploaded(entry: UploadEntry) => voidA file has been fully uploaded.
size"sm" | "md" | "lg"—

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

CSS variables

These CSS variables are how you adapt it. Declare them on the component's selector, not on :root: the stylesheet redeclares them in the rule itself, and a declaration on the same element always wins.

VariableDefault
--amset-uploader-gapvar(--amset-space-3)

How to change it

.amset-uploader {
  --amset-uploader-gap: ...;
}

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: