Forms uploadfile

Drag & Drop Upload

Large interactive file upload zone.

Preview

Drag files here or browse

Usage

Copy the full block below to use this component with its imports.

astro
---
import { Drag&DropUpload } from 'astro-component-kit';
---

<DragDropUpload id="files" accept="image/*" multiple />
--- import { Drag&DropUpload } from 'astro-component-kit'; --- <DragDropUpload id="files" accept="image/*" multiple />

Manual Installation

If you are not using the npm package, create a new file src/components/lib/Drag&DropUpload.astro and paste the following code:

astro
---
/**
 * DragDropUpload — A prominent file upload zone with drag-and-drop support and visual feedback.
 * 
 * @param {string} label - Primary instruction text. Default is "Drag files here or browse".
 * @param {string} id - HTML ID for the file input.
 * @param {string} name - HTML name binding.
 * @param {string} accept - Allowed file types (e.g. "image/*").
 * @param {boolean} multiple - Enable multiple file selection.
 */
interface Props {
  label?: string;
  id: string;
  name?: string;
  accept?: string;
  multiple?: boolean;
}

const { label = "Drag files here or browse", id, name, accept, multiple = false } = Astro.props;
---

<div class="upload-zone" data-upload-zone>
  <div class="upload-zone__icon" aria-hidden="true">
    <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
      <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
      <polyline points="17 8 12 3 7 8"/>
      <line x1="12" y1="3" x2="12" y2="15"/>
    </svg>
  </div>
  <p class="upload-zone__text">
    {label.split('browse').map((part, i) => (
      i === 0 ? <span>{part} <span class="upload-zone__link">browse</span></span> : part
    ))}
  </p>
  <input type="file" {id} {name} {accept} {multiple} hidden class="upload-zone__input" data-upload-input />
</div>

<style>
  .upload-zone { 
    border: 2px dashed var(--c-border, rgba(255,255,255,0.1)); 
    border-radius: var(--r-xl, 20px); 
    padding: var(--sp-12, 3rem); 
    text-align: center; 
    background: var(--c-bg-elev, rgba(255,255,255,0.02)); 
    cursor: pointer; 
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 
    color: var(--c-text-2, #94a3b8); 
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-4, 1rem);
  }
  
  .upload-zone:hover, .upload-zone--dragging { 
    border-color: var(--c-primary, #6366f1); 
    background: rgba(99, 102, 241, 0.05); 
    color: var(--c-text-1, #fff); 
    transform: translateY(-2px);
  }
  
  .upload-zone__icon { color: var(--c-primary, #6366f1); transition: transform 0.3s; }
  .upload-zone:hover .upload-zone__icon { transform: translateY(-5px); }
  
  .upload-zone__text { font-size: 1rem; margin: 0; }
  .upload-zone__link { color: var(--c-primary, #6366f1); font-weight: 700; text-decoration: underline; text-underline-offset: 4px; }
</style>

<script>
  document.querySelectorAll('[data-upload-zone]').forEach(zone => {
    const input = zone.querySelector('[data-upload-input]') as HTMLInputElement;

    zone.addEventListener('click', () => input.click());

    zone.addEventListener('dragover', (e) => {
      e.preventDefault();
      zone.classList.add('upload-zone--dragging');
    });

    ['dragleave', 'drop'].forEach(evt => {
      zone.addEventListener(evt, () => zone.classList.remove('upload-zone--dragging'));
    });

    zone.addEventListener('drop', (e) => {
      e.preventDefault();
      if ((e as DragEvent).dataTransfer?.files) {
        input.files = (e as DragEvent).dataTransfer!.files;
        // Trigger change
        input.dispatchEvent(new Event('change'));
      }
    });
  });
</script>
--- /** * DragDropUpload — A prominent file upload zone with drag-and-drop support and visual feedback. * * @param {string} label - Primary instruction text. Default is "Drag files here or browse". * @param {string} id - HTML ID for the file input. * @param {string} name - HTML name binding. * @param {string} accept - Allowed file types (e.g. "image/*"). * @param {boolean} multiple - Enable multiple file selection. */ interface Props { label?: string; id: string; name?: string; accept?: string; multiple?: boolean; } const { label = "Drag files here or browse", id, name, accept, multiple = false } = Astro.props; --- <div class="upload-zone" data-upload-zone> <div class="upload-zone__icon" aria-hidden="true"> <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="17 8 12 3 7 8"/> <line x1="12" y1="3" x2="12" y2="15"/> </svg> </div> <p class="upload-zone__text"> {label.split('browse').map((part, i) => ( i === 0 ? <span>{part} <span class="upload-zone__link">browse</span></span> : part ))} </p> <input type="file" {id} {name} {accept} {multiple} hidden class="upload-zone__input" data-upload-input /> </div> <style> .upload-zone { border: 2px dashed var(--c-border, rgba(255,255,255,0.1)); border-radius: var(--r-xl, 20px); padding: var(--sp-12, 3rem); text-align: center; background: var(--c-bg-elev, rgba(255,255,255,0.02)); cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); color: var(--c-text-2, #94a3b8); display: flex; flex-direction: column; align-items: center; gap: var(--sp-4, 1rem); } .upload-zone:hover, .upload-zone--dragging { border-color: var(--c-primary, #6366f1); background: rgba(99, 102, 241, 0.05); color: var(--c-text-1, #fff); transform: translateY(-2px); } .upload-zone__icon { color: var(--c-primary, #6366f1); transition: transform 0.3s; } .upload-zone:hover .upload-zone__icon { transform: translateY(-5px); } .upload-zone__text { font-size: 1rem; margin: 0; } .upload-zone__link { color: var(--c-primary, #6366f1); font-weight: 700; text-decoration: underline; text-underline-offset: 4px; } </style> <script> document.querySelectorAll('[data-upload-zone]').forEach(zone => { const input = zone.querySelector('[data-upload-input]') as HTMLInputElement; zone.addEventListener('click', () => input.click()); zone.addEventListener('dragover', (e) => { e.preventDefault(); zone.classList.add('upload-zone--dragging'); }); ['dragleave', 'drop'].forEach(evt => { zone.addEventListener(evt, () => zone.classList.remove('upload-zone--dragging')); }); zone.addEventListener('drop', (e) => { e.preventDefault(); if ((e as DragEvent).dataTransfer?.files) { input.files = (e as DragEvent).dataTransfer!.files; // Trigger change input.dispatchEvent(new Event('change')); } }); }); </script>

Quick Info

Category
Forms
Filename
Drag&DropUpload.astro
Dependencies
None — pure Astro + CSS
Tags
uploadfile