File pickers and drop zones

<input type="file"> with drop events

Baseline widely available
  • Chrome3
  • Edge12
  • Firefox3.6
  • Safari4

Features it needs

A file input already handles choosing files, restricting types through accept, taking several through multiple, and opening the camera on a phone through capture. Adding drag and drop to it is two listeners: preventDefault on dragover so the browser stops treating the drop as navigation, and reading event.dataTransfer.files on drop. What the libraries add on top is previews, validation and upload orchestration.

When this applies

Letting someone pick or drop files.

The native approach

<input type="file" id="picker" accept="image/*" multiple>

<script>
  const zone = document.getElementById("zone");

  // Required, or the browser navigates to the dropped file.
  zone.addEventListener("dragover", (event) => event.preventDefault());

  zone.addEventListener("drop", (event) => {
    event.preventDefault();
    handleFiles(event.dataTransfer.files);
  });
</script>

MDN reference

When the dependency is still right

An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.

  • You accept dropped folders and walk their contents. That needs webkitGetAsEntry on the dropped items, which the drop event exposes but does not traverse for you, and the recursion is real work.
  • You want upload progress, chunking, retries or a queue. dropzone does the transfer as well as the picking, and none of that comes from the input.
  • You render thumbnails and per-file validation state. Reading the files is the easy half; the preview list and its error handling is most of what these libraries are.
  • You need the drop target to accept files dragged from another application on some older targets, where dataTransfer.items behaves inconsistently.

Signs it was hand-rolled

No package is involved in any of these, so nothing would match in a package.json. If the code looks like one of them, this rule applies anyway, and the conditions above still decide.

  • a hidden file input triggered by click() from a styled button, wrapped in a component that forwards the change event
  • dragenter and dragleave counters kept in state to work out whether the pointer is still over the drop zone

Packages this covers