Vesture

Inputs

FileUpload

A drag-and-drop file upload queue with per-file progress, client-side validation, and a remove action — the upload mechanism itself is entirely up to you.

FileUpload

Preview

Drag and drop files here, or click to browseAccepted: image/*,.pdfMax size: 5.0 MB

Usage

tsx
import { FileUpload } from "@vesture/react";
import type { UploadFile } from "@vesture/react";

function Example() {
  const [files, setFiles] = useState<UploadFile[]>([]);

  const handleFilesAdded = (added: File[]) => {
    const entries: UploadFile[] = added.map((file) => ({
      id: crypto.randomUUID(),
      file,
      status: "uploading",
      progress: 0,
    }));
    setFiles((prev) => [...prev, ...entries]);
    // Kick off your own upload per entry, then update its status/progress
    // in files as it proceeds — FileUpload never does this part for you.
  };

  return (
    <FileUpload
      files={files}
      onFilesAdded={handleFilesAdded}
      onRemove={(id) => setFiles((prev) => prev.filter((f) => f.id !== id))}
      maxSize={5 * 1024 * 1024}
      accept="image/*,.pdf"
    />
  );
}

Behavior

  • FileUpload is a controlled queue UI only — it has no upload logic or network code of any kind. onFilesAdded hands you the raw File[] the moment they're dropped or picked; you own assigning ids, performing the actual upload however your backend works, and pushing status/progress updates back into the files prop as it proceeds.
  • Files exceeding maxSize, or that would push the queue past maxFiles, are rejected client-side before onFilesAdded ever fires — a synthesized status: "error" row appears in the queue with a message like "File exceeds 10MB limit", so a rejection is visible rather than silently dropped.
  • Each row reuses Progress for its bar: indeterminate while status is "pending", determinate while "uploading"/"success", replaced by the error message (no bar) when "error".
  • The dropzone is keyboard-focusable and Enter/Space-activatable to open the native file picker — drag-and-drop has no keyboard equivalent, so the picker is the accessible path and works without a mouse.

Props

PropTypeDefaultDescription
filesUploadFile[]Required. The controlled list — { id, file, status, progress, error? } per row. You own this state entirely.
onFilesAdded(files: File[]) => voidFires with newly added, already-validated files — before any upload logic runs.
onRemove(id: string) => voidFires when a row's remove button is clicked, regardless of status; you decide whether to cancel an in-flight upload.
acceptstringNative file input accept attribute, e.g. "image/*,.pdf".
multiplebooleantrueAllows selecting/dropping more than one file at a time.
maxSizenumberBytes. Files over this size are rejected client-side with a synthesized error row.
maxFilesnumberRejects files that would push files.length past this total.
disabledbooleanDisables the dropzone and file picker.