Skip to content

How to implement file upload with drag and drop in vanilla JS

I wrote about the Drag and Drop API concepts in the past.

Now I want to show how I implemented a simple file upload with drag and drop on a site I’m building.

Identify an HTML element you want people to drag their files to:

<div
  class="dropzone"
  ondragover=`dragOverHandler(event)`
  ondragleave=`dragLeaveHandler(event)`
  ondrop=`dropHandler(event)` 
>
...
</div>

ondragover is fired when people are dragging a file on the element. We can use this to add some style, for example a dashed border.

ondragleave is the opposite, when we exit the drop area.

I used this JS to add a dragging_over class to the element, and style it with CSS:

function dragOverHandler(event) {
  event.preventDefault();
  const dropzone = document.querySelector('#dropzone');
  dropzone.classList.add('dragging_over');
}

function dragLeaveHandler(event) {
  event.preventDefault();
  const dropzone = document.querySelector('#dropzone');
  dropzone.classList.remove('dragging_over');
}
#dropzone.dragging_over {
  border: 2px dashed #fff;
  background-color: #666;
}

ondrop is fired when the file (or multiple files!) is dropped on the area.

That’s where the action happens.

I gather the files, check they’re images (I only want images in this example), and POST the data to /api/upload:

async function dropHandler(event) {
  event.preventDefault()

  const endpoint = `/api/upload`

  if (event.dataTransfer.items) {
    const formData = new FormData()
    formData.append('action', 'upload')

    for (let item of event.dataTransfer.items) {
      if (item.kind === 'file') {
        const file = item.getAsFile()
        if (file) { 
          //I only want images
          if (!file.type.match('image.*')) {
            alert('only images supported')
            return
          }
          formData.append('files', file)
        }
      }
    }

    try {
      const response = await fetch(endpoint, {
        method: 'POST',
        body: formData,
      })

      if (response.ok) {
        console.log('File upload successful')
      } else {
        console.error('File upload failed', response)
      }
    } catch (error) {
      console.error('Error uploading file', error)
    }
  }
}

How to handle that server-side depends on your server.

With Astro I got the data using:

const formData = await Astro.request.formData()

console.log(formData.getAll('files'))

→ Here's my latest YouTube video

→ Get my JavaScript Beginner's Handbook

→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter

JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025

Bootcamp 2025

Join the waiting list