---
title: File input (Vanilla) - USWDS + Tailwind
description: Let people attach one or more files with a file input. See examples
  for accepted file types and multiple uploads.
---

# File input

Let people attach one or more files with a file input. See examples for accepted file types and multiple uploads.

This component requires the following JavaScript plugin:

* [File Input](https://v2.uswds-tailwind.com/docs/vanilla/javascript#file-input)

## Examples

### Default

```html
<div
  data-scope="file-input" data-part="root"
  class="relative z-0 max-w-lg"
>
  <label
    data-scope="file-input" data-part="label"
    class="block"
  >Input accepts a single file</label>
  <div>
    <div data-scope="file-input" data-part="sr-status"></div>
      <div
        data-scope="file-input" data-part="dropzone"
        class="border border-dashed border-gray-30 hover:border-gray-50 group data-[invalid]:border-orange-30v mt-2 relative w-full bg-white data-[dragging]:bg-blue-10"
      >
        <div
          data-scope="file-input" data-part="item-group"
          class="relative z-30 pointer-events-none hidden data-[valid]:block data-[valid]:bg-blue-10 group"
        >
          <div class="flex justify-between items-center p-2">
            <div
              data-scope="file-input" data-part="preview-heading"
              class="font-bold"
            ></div>
          </div>
          <div
            data-scope="file-input" data-part="item"
            class="flex gap-2 p-2 border-t border-t-white group-data-[dragging]:opacity-10"
          >
            <img data-scope="file-input" data-part="item-preview-image" alt="" class="size-8 object-contain" />
            <div data-file-name class="flex items-center"></div>
          </div>
        </div>
        <div
          data-scope="file-input" data-part="instructions"
          class="py-8 px-4 pointer-events-none z-30 relative text-center data-[valid]:hidden"
        >
          <span data-scope="file-input" data-part="drag-text">Drag file here or</span>
          <span data-scope="file-input" data-part="choose" class="text-blue-60v underline">choose from folder</span>
        </div>
        <div
          data-scope="file-input" data-part="error-text"
          class="hidden text-red-60v font-bold data-[invalid]:block -mt-6 mb-6 text-center"
        ></div>
      <input
        data-scope="file-input" data-part="input"
        id="file-input-single"
        class="cursor-pointer absolute inset-0 z-10 p-2 focus:outline-4 focus:outline-blue-40v [&::-webkit-file-upload-button]:hidden data-[dragging]:bg-blue-10 text-transparent"
        type="file"
        aria-label="Drag file here or choose from folder"
      />
    </div>
  </div>
</div>
```

### Specific File Types

```html
<div
  data-scope="file-input" data-part="root"
  class="relative z-0 max-w-lg"
>
  <label
    data-scope="file-input" data-part="label"
    class="block"
  >Input accepts only specific file types</label>
  <span class="text-gray-50" id="file-input-specific-hint">Select PDF or TXT files</span>
  <div>
    <div data-scope="file-input" data-part="sr-status"></div>
    <div
      data-scope="file-input" data-part="dropzone"
      class="border border-dashed border-gray-30 hover:border-gray-50 group data-[invalid]:border-orange-30v mt-2 relative w-full bg-white data-[dragging]:bg-blue-10"
    >
      <div
        data-scope="file-input" data-part="item-group"
        class="relative z-30 pointer-events-none hidden data-[valid]:block data-[valid]:bg-blue-10 group"
      >
        <div class="flex justify-between items-center p-2">
          <div
            data-scope="file-input" data-part="preview-heading"
            class="font-bold"
          ></div>
        </div>
        <div
          data-scope="file-input" data-part="item"
          class="flex gap-2 p-2 border-t border-t-white group-data-[dragging]:opacity-10"
        >
          <img data-scope="file-input" data-part="item-preview-image" alt="" class="size-8 object-contain" />
          <div data-file-name class="flex items-center"></div>
        </div>
      </div>
      <div
        data-scope="file-input" data-part="instructions"
        class="py-8 px-4 pointer-events-none z-30 relative text-center data-[valid]:hidden"
      >
        <span data-scope="file-input" data-part="drag-text">Drag file here or</span>
        <span data-scope="file-input" data-part="choose" class="text-blue-60v underline">choose from folder</span>
      </div>
      <div
        data-scope="file-input" data-part="error-text"
        class="hidden text-red-60v font-bold data-[invalid]:block -mt-6 mb-6 text-center"
      ></div>
      <input
        data-scope="file-input" data-part="input"
        id="file-input-specific"
        class="cursor-pointer absolute inset-0 z-10 p-2 focus:outline-4 focus:outline-blue-40v [&::-webkit-file-upload-button]:hidden data-[dragging]:bg-blue-10 text-transparent"
        type="file"
        aria-label="Drag file here or choose from folder"
        aria-describedby="file-input-specific-hint"
        accept=".pdf,.txt"
      />
    </div>
  </div>
</div>
```

### Accept Multiple Files

```html
<div
  data-scope="file-input" data-part="root"
  class="relative z-0 max-w-lg"
>
  <label
    data-scope="file-input" data-part="label"
    class="block"
  >Input accepts multiple files</label>
  <span class="text-gray-50" id="file-input-many-hint">Select one or more files</span>
  <div>
    <div data-scope="file-input" data-part="sr-status"></div>
    <div
      data-scope="file-input" data-part="dropzone"
      class="border border-dashed border-gray-30 hover:border-gray-50 group data-[invalid]:border-orange-30v mt-2 relative w-full bg-white data-[dragging]:bg-blue-10"
    >
      <div
        data-scope="file-input" data-part="item-group"
        class="relative z-30 pointer-events-none hidden data-[valid]:block data-[valid]:bg-blue-10 group"
      >
        <div class="flex justify-between items-center p-2">
          <div
            data-scope="file-input" data-part="preview-heading"
            class="font-bold"
          ></div>
        </div>
        <div
          data-scope="file-input" data-part="item"
          class="flex gap-2 p-2 border-t border-t-white group-data-[dragging]:opacity-10"
        >
          <img data-scope="file-input" data-part="item-preview-image" alt="" class="size-8 object-contain" />
          <div data-file-name class="flex items-center"></div>
        </div>
      </div>
      <div
        data-scope="file-input" data-part="instructions"
        class="py-8 px-4 pointer-events-none z-30 relative text-center data-[valid]:hidden"
      >
        <span data-scope="file-input" data-part="drag-text">Drag file here or</span>
        <span data-scope="file-input" data-part="choose" class="text-blue-60v underline">choose from folder</span>
      </div>
      <div
        data-scope="file-input" data-part="error-text"
        class="hidden text-red-60v font-bold data-[invalid]:block -mt-6 mb-6 text-center"
      ></div>
      <input
        data-scope="file-input" data-part="input"
        id="file-input-many"
        class="cursor-pointer absolute inset-0 z-10 p-2 focus:outline-4 focus:outline-blue-40v [&::-webkit-file-upload-button]:hidden data-[dragging]:bg-blue-10 text-transparent"
        type="file"
        aria-label="Drag file here or choose from folder"
        aria-describedby="file-input-many-hint"
        multiple
      />
    </div>
  </div>
</div>
```

## Component API

### Initialization

```js
import { fileInputInit } from '@uswds-tailwind/compat/file-input'

fileInputInit()
```

`FileInput` is the corresponding instance class. Initialize after all parts and the preview template exist.

### Anatomy

Use `data-scope="file-input" data-part="root"` on the wrapper. Required descendants are `input`, `dropzone`, `instructions`, and `item-group`. The `item-group` must contain an initial `item` template with an `item-preview-image` image and an element with `data-file-name` for the filename. Initialization removes this template and clones it for selected files.

Optional parts are `label`, `box`, `drag-text`, `choose`, `preview-heading`, `error-text`, and `sr-status`. Include a visible label, error text, and status region in accessible examples. The runtime writes selection copy and announcement text into these parts.

| Attribute | Description |
| - | - |
| `accept` | Comma-separated file types; the `accept` option takes precedence. A rejected file rejects the entire batch and clears the accepted selection. |
| `disabled` | Disables the native picker; the `disabled` option takes precedence. |
| `multiple` | Allows multiple files in the native picker; the `multiple` option takes precedence. |
| `aria-disabled` | Presence, including `false`, supplies the fallback for `ariaDisabled`. This option marks the control without blocking interaction. |
| `data-errormessage` | Invalid-type message; the `errorText` option takes precedence. Defaults to `Error: This is not a valid file type.` |
| `name` | Native form submission name. |

The runtime generates `data-dragging` on the dropzone, `data-invalid` on the dropzone and error text, and `data-valid` on the item group. It hides instructions when files are selected and hides the item group when no files are selected.

Preview images use object URLs. Failed image loads receive `data-preview-type` with `pdf`, `word`, `excel`, `video`, or `generic` for fallback styling.

### Configuration and methods

Use `FileInput.getOrCreateInstance(root, options)` to pass `accept`, `multiple`, `disabled`, `ariaDisabled`, `errorText`, `id`, or `ids`. Options take precedence over markup fallbacks.

`setFiles(files)` accepts a `File[]` and returns `Promise<void>` after the queued selection update. It applies client-side type validation and updates previews; it does not populate the native file input’s `files`. Use the native picker for normal form submission. `api.acceptedFiles`, `api.invalid`, and `api.errorText` expose selection state. This component has no selection-change callback.

`accept` is a file-picker hint and client-side constraint. The component does not upload files. Your application owns submission and server validation.

### Cleanup and accessibility

`fileInputInit()` returns initialized instances. Call `destroy()` before removing markup to release preview URLs and stop the machine. Removed previews also release their URLs. See [instance lifecycle](https://v2.uswds-tailwind.com/docs/vanilla/javascript#instance-lifecycle).

The separate status region uses `role="status"` and `aria-live="polite"`. Selection announcements follow a one-second delay. An initially disabled component hides this region. Keep hints linked to the input and retain visible focus styles.
