File Upload Components

Three patterns: minimal input, a polished file:-styled control, and a drag-and-drop card with Heroicons. Previews are UI-only; use the snippets to wire up behavior.

1) Minimal File Input

Plain browser file picker with good spacing and helpers.

Max 5MB. Supported: JPG, PNG, PDF.

<form class="space-y-3 max-w-xl">
  <label for="file-minimal" class="block text-sm font-medium text-gray-900">Upload file</label>
  <input id="file-minimal" name="file-minimal" type="file" class="block w-full text-sm text-gray-900 file:mr-4 file:px-3 file:py-2 file:rounded-md file:border file:border-black/10 file:bg-white file:text-gray-900 hover:file:bg-gray-50" />
  <p class="text-xs text-gray-500">Max 5MB. Supported: JPG, PNG, PDF.</p>
  <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Submit</button>
</form>
<template>
  <form class="space-y-3 max-w-xl">
    <label for="file-minimal" class="block text-sm font-medium text-gray-900">Upload file</label>
    <input id="file-minimal" name="file-minimal" type="file" class="block w-full text-sm text-gray-900 file:mr-4 file:px-3 file:py-2 file:rounded-md file:border file:border-black/10 file:bg-white file:text-gray-900 hover:file:bg-gray-50" />
    <p class="text-xs text-gray-500">Max 5MB. Supported: JPG, PNG, PDF.</p>
    <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Submit</button>
  </form>
</template>
<script setup>
// UI-only
</script>
export default function MinimalFileInput(){
  return (
    <form className="space-y-3 max-w-xl">
      <label htmlFor="file-minimal" className="block text-sm font-medium text-gray-900">Upload file</label>
      <input id="file-minimal" name="file-minimal" type="file" className="block w-full text-sm text-gray-900 file:mr-4 file:px-3 file:py-2 file:rounded-md file:border file:border-black/10 file:bg-white file:text-gray-900 hover:file:bg-gray-50" />
      <p className="text-xs text-gray-500">Max 5MB. Supported: JPG, PNG, PDF.</p>
      <button type="button" className="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Submit</button>
    </form>
  )
}

2) Styled File Input (Tailwind file:)

Custom button via file: variants, helper text, and disabled state preview.

PNG or JPG up to 2MB.

<form class="space-y-6 max-w-xl">
  <div>
    <label for="file-styled" class="block text-sm font-medium text-gray-900 mb-1">Profile photo</label>
    <input id="file-styled" name="file-styled" type="file" accept="image/*"
      class="block w-full text-sm text-gray-900
             file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5
             file:bg-violet/10 file:text-violet-700 hover:file:bg-violet/15
             file:ring-1 file:ring-inset file:ring-violet/20" />
    <p class="mt-2 text-xs text-gray-500">PNG or JPG up to 2MB.</p>
  </div>

  <div class="opacity-60">
    <label for="file-styled-disabled" class="block text-sm font-medium text-gray-900 mb-1">Disabled state</label>
    <input id="file-styled-disabled" name="file-styled-disabled" type="file" disabled
      class="block w-full cursor-not-allowed text-sm text-gray-500
             file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5
             file:bg-gray-100 file:text-gray-500" />
  </div>

  <div class="flex gap-3 pt-2">
    <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Save</button>
    <button type="button" class="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
  </div>
</form>
<template>
  <form class="space-y-6 max-w-xl">
    <div>
      <label for="file-styled" class="block text-sm font-medium text-gray-900 mb-1">Profile photo</label>
      <input id="file-styled" name="file-styled" type="file" accept="image/*"
        class="block w-full text-sm text-gray-900 file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5 file:bg-violet/10 file:text-violet-700 hover:file:bg-violet/15 file:ring-1 file:ring-inset file:ring-violet/20" />
      <p class="mt-2 text-xs text-gray-500">PNG or JPG up to 2MB.</p>
    </div>
    <div class="opacity-60">
      <label for="file-styled-disabled" class="block text-sm font-medium text-gray-900 mb-1">Disabled state</label>
      <input id="file-styled-disabled" name="file-styled-disabled" type="file" disabled
        class="block w-full cursor-not-allowed text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5 file:bg-gray-100 file:text-gray-500" />
    </div>
    <div class="flex gap-3 pt-2">
      <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Save</button>
      <button type="button" class="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
    </div>
  </form>
</template>
<script setup>
// UI-only
</script>
export default function StyledFileInput(){
  return (
    <form className="space-y-6 max-w-xl">
      <div>
        <label htmlFor="file-styled" className="block text-sm font-medium text-gray-900 mb-1">Profile photo</label>
        <input id="file-styled" name="file-styled" type="file" accept="image/*" className="block w-full text-sm text-gray-900 file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5 file:bg-violet/10 file:text-violet-700 hover:file:bg-violet/15 file:ring-1 file:ring-inset file:ring-violet/20" />
        <p className="mt-2 text-xs text-gray-500">PNG or JPG up to 2MB.</p>
      </div>
      <div className="opacity-60">
        <label htmlFor="file-styled-disabled" className="block text-sm font-medium text-gray-900 mb-1">Disabled state</label>
        <input id="file-styled-disabled" name="file-styled-disabled" type="file" disabled className="block w-full cursor-not-allowed text-sm text-gray-500 file:mr-4 file:rounded-lg file:border-0 file:px-4 file:py-2.5 file:bg-gray-100 file:text-gray-500" />
      </div>
      <div className="flex gap-3 pt-2">
        <button type="button" className="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Save</button>
        <button type="button" className="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
      </div>
    </form>
  )
}

3) Drag & Drop Upload (Heroicons)

Dashed dropzone using an inline Heroicon. Click or drop files. Multiple supported.

Up to 10 files. Max 10MB each. PDF, PNG, JPG.

invoice-2025-01.pdf

842 KB

design-mockup.png

2.1 MB

<form class="max-w-2xl">
  <label class="relative block w-full rounded-2xl border-2 border-dashed border-gray-300 p-8 text-center hover:border-gray-400 transition-colors cursor-pointer">
    <div class="mx-auto h-12 w-12 text-violet">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" class="h-12 w-12" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M15.75 10.5L12 6.75 8.25 10.5M12 6.75V15.75" />
        <path d="M6.75 19.5h10.5a3.75 3.75 0 0 0 0-7.5h-.278A5.251 5.251 0 0 0 6.75 9.75h-.75a4.5 4.5 0 0 0 0 9h.75z" />
      </svg>
    </div>
    <div class="mt-3">
      <p class="text-sm font-semibold text-gray-900">Drag & drop files here</p>
      <p class="text-sm text-gray-600">or <span class="text-violet">browse</span> your computer</p>
    </div>
    <input type="file" multiple class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Upload files">
  </label>
  <p class="mt-3 text-xs text-gray-500">Up to 10 files. Max 10MB each. PDF, PNG, JPG.</p>

  <div class="mt-6 grid gap-3 sm:grid-cols-2">
    <div class="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
      <div class="min-w-0">
        <p class="truncate text-sm font-medium text-gray-900">invoice-2025-01.pdf</p>
        <p class="text-xs text-gray-500">842 KB</p>
      </div>
      <button type="button" class="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
    </div>
    <div class="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
      <div class="min-w-0">
        <p class="truncate text-sm font-medium text-gray-900">design-mockup.png</p>
        <p class="text-xs text-gray-500">2.1 MB</p>
      </div>
      <button type="button" class="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
    </div>
  </div>

  <div class="mt-6 flex gap-3">
    <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Upload</button>
    <button type="button" class="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
  </div>
</form>
<template>
  <form class="max-w-2xl">
    <label class="relative block w-full rounded-2xl border-2 border-dashed border-gray-300 p-8 text-center hover:border-gray-400 transition-colors cursor-pointer">
      <div class="mx-auto h-12 w-12 text-violet">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" class="h-12 w-12" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <path d="M15.75 10.5L12 6.75 8.25 10.5M12 6.75V15.75" />
          <path d="M6.75 19.5h10.5a3.75 3.75 0 0 0 0-7.5h-.278A5.251 5.251 0 0 0 6.75 9.75h-.75a4.5 4.5 0 0 0 0 9h.75z" />
        </svg>
      </div>
      <div class="mt-3">
        <p class="text-sm font-semibold text-gray-900">Drag & drop files here</p>
        <p class="text-sm text-gray-600">or <span class="text-violet">browse</span> your computer</p>
      </div>
      <input type="file" multiple class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Upload files" />
    </label>
    <p class="mt-3 text-xs text-gray-500">Up to 10 files. Max 10MB each. PDF, PNG, JPG.</p>

    <div class="mt-6 grid gap-3 sm:grid-cols-2">
      <div class="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
        <div class="min-w-0">
          <p class="truncate text-sm font-medium text-gray-900">invoice-2025-01.pdf</p>
          <p class="text-xs text-gray-500">842 KB</p>
        </div>
        <button type="button" class="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
      </div>
      <div class="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
        <div class="min-w-0">
          <p class="truncate text-sm font-medium text-gray-900">design-mockup.png</p>
          <p class="text-xs text-gray-500">2.1 MB</p>
        </div>
        <button type="button" class="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
      </div>
    </div>

    <div class="mt-6 flex gap-3">
      <button type="button" class="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Upload</button>
      <button type="button" class="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
    </div>
  </form>
</template>
<script setup>
// UI-only
</script>
export default function DragDropUpload(){
  return (
    <form className="max-w-2xl">
      <label className="relative block w-full rounded-2xl border-2 border-dashed border-gray-300 p-8 text-center hover:border-gray-400 transition-colors cursor-pointer">
        <div className="mx-auto h-12 w-12 text-violet">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="h-12 w-12" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="M15.75 10.5L12 6.75 8.25 10.5M12 6.75V15.75" />
            <path d="M6.75 19.5h10.5a3.75 3.75 0 0 0 0-7.5h-.278A5.251 5.251 0 0 0 6.75 9.75h-.75a4.5 4.5 0 0 0 0 9h.75z" />
          </svg>
        </div>
        <div className="mt-3">
          <p className="text-sm font-semibold text-gray-900">Drag & drop files here</p>
          <p className="text-sm text-gray-600">or <span className="text-violet">browse</span> your computer</p>
        </div>
        <input type="file" multiple className="absolute inset-0 opacity-0 cursor-pointer" aria-label="Upload files" />
      </label>

      <p className="mt-3 text-xs text-gray-500">Up to 10 files. Max 10MB each. PDF, PNG, JPG.</p>

      <div className="mt-6 grid gap-3 sm:grid-cols-2">
        <div className="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
          <div className="min-w-0">
            <p className="truncate text-sm font-medium text-gray-900">invoice-2025-01.pdf</p>
            <p className="text-xs text-gray-500">842 KB</p>
          </div>
          <button type="button" className="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
        </div>
        <div className="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2">
          <div className="min-w-0">
            <p className="truncate text-sm font-medium text-gray-900">design-mockup.png</p>
            <p className="text-xs text-gray-500">2.1 MB</p>
          </div>
          <button type="button" className="rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50">Remove</button>
        </div>
      </div>

      <div className="mt-6 flex gap-3">
        <button type="button" className="inline-flex items-center justify-center rounded-md bg-violet px-4 py-2 text-sm font-semibold text-white hover:brightness-110 border border-black/10">Upload</button>
        <button type="button" className="inline-flex items-center justify-center rounded-md border border-black/10 bg-white px-4 py-2 text-sm font-semibold text-gray-900 hover:bg-gray-50">Cancel</button>
      </div>
    </form>
  )
}

Notes

  • Use the file: variant to style the native file button without extra wrappers.
  • The drag-and-drop card uses an invisible input covering the label so the whole surface is clickable.
  • Inline Heroicon is the 24/outline “CloudArrowUp” path; swap any icon from Heroicons the same way.

We use cookies to improve your experience and analytics. You can accept all cookies or reject non-essential ones.

Learn More