Basic Dropzone
A dashed drop area with a real file input that stays reachable by keyboard and screen reader.
3 frameworksBeginner
A single labelled button that opens the file picker and reports the chosen file name inline.
<!--
The label IS the button - no scripted click-forwarding to a hidden input.
The input sits underneath as a peer (sr-only, so it keeps its tab stop), and
peer-focus-visible paints the ring on the label when the real input has
focus. The selected name is rendered by the React tabs.
-->
<div class="flex w-full max-w-md flex-wrap items-center gap-3">
<input id="file-button-input" type="file" class="peer sr-only" />
<label
for="file-button-input"
class="inline-flex min-h-10 cursor-pointer items-center justify-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 peer-focus-visible:ring-2 peer-focus-visible:ring-blue-600 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-white motion-reduce:transition-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-100 dark:peer-focus-visible:ring-blue-400 dark:peer-focus-visible:ring-offset-gray-950"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="h-4 w-4" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.375 12.74 11.7 19.415a4.5 4.5 0 0 1-6.364-6.364l8.485-8.486a3 3 0 0 1 4.243 4.243l-8.132 8.132a1.5 1.5 0 0 1-2.121-2.121l6.293-6.293" />
</svg>
Choose file
</label>
<span aria-live="polite" class="min-w-0 flex-1 truncate text-sm text-gray-500 dark:text-gray-400">
No file selected
</span>
</div>| Prop | Type | Default | Description |
|---|---|---|---|
label | string | 'Choose file' | Accessible label announced while loading. |
accept | string | '' | Accept |
multiple | boolean | false | Multiple |
onFiles | (files: File[]) => void | - | On files |
className | string | - | Additional classes merged onto the root element. |
The label IS the button - no scripted click on a hidden input. The real input sits underneath as a `peer` (`sr-only`, so it keeps its tab stop) and `peer-focus-visible` paints the ring from genuine input focus. The name summary is `aria-live`, since it is the component's entire feedback.