Skip to content

File Upload Dropzone

Tailwind CSS drag-and-drop file upload zone with keyboard support, type and size validation, a file list and per-file progress bars.

Live preview

Open in new tab

Code

A drag-and-drop file upload dropzone built with Tailwind CSS and vanilla JavaScript. Users can drop files onto the dashed area or pick them with the native file dialog, and every file appears in a list with its own progress bar, size and remove button. It fits attachment fields, project settings, support forms and media libraries.

How it works

The root is .tfd-dropzone. The drop area is a label (.tfd-zone, marked data-tfd-zone) that wraps a visually hidden input type="file" with multiple and an accept list. Because the input is only hidden with sr-only, clicking the label or pressing Enter/Space on the focused input opens the file dialog, and has-[input:focus-visible] draws a focus ring around the whole zone.

The script listens for dragenter, dragover, dragleave and drop on the zone. A drag counter keeps the tfd-zone--active highlight from flickering while the pointer moves over child elements. Dropped or selected files are passed to addFile(), which clones the row from the <template data-tfd-template>, fills in the extension badge, name and size, and appends it to data-tfd-list.

Each file is checked against the input's accept attribute and the size limit. Invalid files get data-state="error" and a red message. Valid files run through simulateUpload(), which raises the bar width and aria-valuenow in steps and then sets data-state="done". The row colors switch with group-data-[state=done] and group-data-[state=error] variants, so no extra CSS classes are toggled. Remove buttons cancel the timer, delete the row and update the counter in data-tfd-summary.

Customization

  • data-tfd-max-mb="10" on .tfd-dropzone: set the maximum file size in megabytes; larger files show "Larger than 10.0 MB". Update the "Max 10 MB" badge to match.
  • accept on #tfd-input: list the allowed extensions or MIME types (.pdf,.png or image/*); dropped files are validated against the same rule.
  • simulateUpload() in script.js: replace the timer with fetch() or an XMLHttpRequest that reports upload.onprogress to setProgress().
  • bg-indigo-500 on .tfd-bar and the indigo classes on the zone: swap for sky, violet or emerald to rebrand.
  • .tfd-zone.tfd-zone--active in style.css: change the border and background shown while files are dragged over.
  • Remove multiple from the input to accept one file at a time.

Accessibility

The hidden input stays in the tab order and is described by the format hint through aria-describedby. Each row has a role="progressbar" with aria-valuemin, aria-valuemax, aria-valuenow and a file-specific aria-label. Remove buttons are labelled "Remove file name", and focus moves to the next row (or back to the input) after a removal. Completed uploads, errors and removals are announced through a role="status" live region. Animations in style.css and the bar transitions are turned off under prefers-reduced-motion: reduce.

Usage notes

For several dropzones on one page, wrap each in its own .tfd-dropzone and give each input a unique id and matching for. The sample row shows the completed state and can be deleted.

Requirements

Add these to your page (already included in the download):

  • <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

The Tailwind browser build is for prototyping. In production, compile Tailwind with its CLI or your bundler.

File Upload Dropzone – Tailwind CSS file uploads component screenshot
File Upload Dropzone · Tailwind CSS File Uploads

More File Uploads

Image Upload with Preview – Bootstrap 5 file uploads preview

Image Upload with Preview

Bootstrap 5 drag-and-drop image uploader with thumbnail previews, remove buttons, type and size validation and simulated upload progress.