Components

Input File

new

A drag-and-drop file field with previews, built on the native file input.

Drop product shots here

PNG, JPG or WebP · up to 4 images, 5 MB each

Installation

npx shadcn@latest add @blank/input-file

Usage

import {
  InputFile,
  InputFileDropzone,
  InputFileItem,
  InputFileList,
  InputFileTrigger,
} from "@/components/ui/input-file"
<InputFile accept="image/*" multiple value={files} onValueChange={setFiles}>
  <InputFileDropzone>
    Drop images here
    <InputFileTrigger>Browse files</InputFileTrigger>
  </InputFileDropzone>
  <InputFileList>
    {files.map((file) => (
      <InputFileItem key={file.name} file={file} />
    ))}
  </InputFileList>
</InputFile>

A visually hidden native <input type="file"> does the real work. It keeps the field in the tab order, opens the system picker on Enter or Space, and holds the same files you see — so a plain form submit sends them with name, and required works without JavaScript validation.

Files that break accept, maxSize or maxFiles never reach the value; onFileReject tells you which rule they broke. Showing that message is Field's job, not the dropzone's, as the next example does.

InputFileItem renders a preview, the name, the size and a remove button. Pass children to lay out your own row from the same parts: InputFileItemPreview, InputFileItemContent, InputFileItemName, InputFileItemSize and InputFileItemRemove.

Examples

In a field

Choose a file or drag it here
PDF only, up to 2 MB.

API Reference

PropTypeDefault
valueFile[]—
defaultValueFile[][]
onValueChange(files: File[]) => void—
acceptstring—
multiplebooleanfalse
maxSizenumber (bytes)—
maxFilesnumber—
onFileReject(file: File, reason: "type" | "size" | "count") => void—
disabledbooleanfalse
name, id, required, aria-invalid, aria-describedby—passed to the native input