๐Ÿ“ JavaScript

File Uploads with Drag & Drop and Previews โ€” Vanilla JS

๐Ÿ“… Jul 4, 2026 โฑ 3 min read

The drag-drop uploader with preview โ€” a portfolio-grade component in ~40 lines.

The drop zone

const zone = document.querySelector(".drop-zone");

["dragover", "dragleave", "drop"].forEach((evt) =>
  zone.addEventListener(evt, (e) => e.preventDefault())   // required! else browser opens the file
);
zone.addEventListener("dragover", () => zone.classList.add("hot"));
zone.addEventListener("dragleave", () => zone.classList.remove("hot"));
zone.addEventListener("drop", (e) => {
  zone.classList.remove("hot");
  handleFiles(e.dataTransfer.files);
});

// clicking the zone opens the picker too
zone.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", () => handleFiles(fileInput.files));

Validate + preview

function handleFiles(files) {
  for (const file of files) {
    if (!file.type.startsWith("image/")) return alert("Images only");
    if (file.size > 2 * 1024 * 1024) return alert("Max 2MB");

    const img = document.createElement("img");
    img.src = URL.createObjectURL(file);      // instant preview, no FileReader needed
    img.onload = () => URL.revokeObjectURL(img.src);   // free the memory
    previews.append(img);
  }
}

Upload

const fd = new FormData();
fd.append("photo", file);
await fetch("/api/upload", { method: "POST", body: fd });   // NO Content-Type header โ€” browser sets it

Validate size/type on the server again โ€” client checks are convenience only.

โ† All Articles