Skip to content
Devix Open Source
UI component v1.0.0 MIT Alpha

File Uploader

Drag and drop, chunked, resumable uploads with validation and progress.

npm install @devix-labs/file-uploader
Vanilla JS React Vue Svelte Laravel
Attach some files Open

Sizes that cannot be misread — a bare number is bytes, '5MB' and '5MiB' say their own unit. Refusals with a code, a limit and an actual value, shown on the file rather than swallowed. Resumable chunked uploads over plain headers and a raw body, against a receiver you can write in twenty lines. Upload speed and time remaining from a rolling average. Dropped folders read to the end, pasted screenshots given their own name. The real file input stays the control, so the keyboard, the screen reader and plain form posting all come free. 8.9 kB gzip, zero dependencies, and the same behaviour in React, Vue, Svelte, a web component or a plain page.

What you get

A size that means something

A bare number is always bytes; '5MB' is 5,000,000 and '5MiB' is 5,242,880. Dropzone's four most-reacted open issues are four reports of the same confusion.

Refusals with reasons

'too-large', 'wrong-type', 'too-many', 'duplicate', 'empty' — each with the limit and the actual value, and the file left in the list saying which rule it broke.

Chunks that resume

A HEAD asks what the server already has, then plain POSTs of raw bytes carry the rest. No tus, no envelope, no base64 — a receiver is twenty lines, and the tests ship one.

Speed and time remaining

A rolling average over the last few seconds, which says nothing rather than claiming a stale rate when the connection stalls. An open request on FilePond.

The input stays the control

A real <input type="file"> with the drop zone as its label — so Tab, Enter, the picker, the screen reader and $_FILES all work with no code at all.

No server needed

Leave out the url and the widget is an enhancement rather than a replacement: the files stay in the input and your form posts them exactly as it would have.

Folders and clipboards

A dropped folder is read to the end, each file keeping its path; a pasted screenshot becomes a file with a name of its own instead of a third image.png.

Retries the right failures

A dropped connection, a 500 or a 429 is tried again with a widening gap. A 422 is the server's answer, so it is asked once and told to you.

File Uploader — overview

Why it exists

The most downloaded uploader on npm does not upload anything — react-dropzone hands you the files and stops, nine and a half million times a week, and everyone writes the rest themselves. Of the ones that do upload: Dropzone's four most-reacted open issues are four separate reports that its size limit is documented in bytes and read as megabytes; FilePond is 34 kB and has had right-to-left support, upload speed and upload order open as requests for years; Uppy is nine packages before you have sent a byte.

And all of them rebuild the file input as a <div>, then bolt the keyboard and the screen reader back on afterwards.

What it does differently

  • The input stays the control. A real <input type="file"> with the drop zone as its label. It is already focusable, already announced, already what a form posts — so Tab, Enter, the picker and $_FILES all work without a line of code. With no url at all, the widget is an enhancement and the form posts the files itself.
  • Sizes cannot be misread. A bare number is bytes. '5MB' is 5,000,000 and '5MiB' is 5,242,880, and the package knows the difference.
  • A refusal is a code, a limit and an actual value — not a sentence to match on. And the file stays in the list saying what it broke, rather than vanishing.
  • Types are matched on the extension too, because Windows calls .csv an Excel file and a file out of an archive often has no type at all.
  • Chunks that resume. A HEAD asks what the server already has; the pieces are plain POSTs of raw bytes with headers. A receiver is twenty lines, and the test suite ships one.
  • Speed and time remaining, averaged over a few seconds, and silent rather than wrong when nothing is moving.
  • Folders read to the end. readEntries returns a hundred at a time and must be called until it returns none — which is why "only 100 files uploaded" is a recurring report elsewhere.
  • 8.9 kB, no dependencies.

Not in 1.0

Remote sources (Dropbox, Google Drive) and direct-to-S3 multipart both need a server component, and a widget that pretends otherwise is lying about what you have to run. Image cropping belongs to the image picker, not here — though transform takes any Blob, so compressing a photo before it goes is a dozen lines using the browser's own canvas.

How it compares

Questions

Do I need a special server?

No. By default each file is one multipart POST — what your server already accepts. Chunking adds a few X- headers and a raw body; the Uploading guide has a complete Laravel receiver in about twenty lines, and the browser tests run against an equivalent in Node.

What happens without JavaScript?

The same as before you added it. The widget enhances a real <input type="file">, and with no url set, the files stay in that input and the form posts them itself.

Can it compress images before upload?

Yes, through transform, which takes any Blob back. The Uploading guide shows it in a dozen lines with the browser's own canvas — the package does not ship an image codec, because the browser already has several.

How does it compare on size?

8.9 kB of JavaScript and 1.3 kB of CSS, gzipped, with no dependencies. Measured the same way on the same day: FilePond is 34.1 kB plus 3.4 kB of CSS, and Dropzone is 11.7 kB.

Does it do S3 multipart or Dropbox?

No, and deliberately. Both need a server component, and a widget that implies otherwise is hiding what you have to run. What is here is the part that talks to your own server.