For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/en/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/en/llms-full.txt, and this page is available as Markdown at https://ifhover.github.io/leaf-ui/en/components/upload.md.

Upload

Choose or drop files and follow upload progress.

Choose and validate

Images, PDF or text · up to 5 files, 5 MB each
basic.tsx
import { Space, Upload } from '@ifhover/leaf-ui';

export function UploadBasic() {
  return (
    <Space direction="vertical" align="stretch">
      <Upload
        multiple
        accept="image/*,.pdf,.txt"
        maxCount={5}
        maxSize={5 * 1024 * 1024}
        hint="Images, PDF or text · up to 5 files, 5 MB each"
      />
    </Space>
  );
}

Drag, progress and cancellation

This demo simulates progress locally. Use action or customRequest to connect your upload service.
advanced.tsx
import type { UploadRequest } from '@ifhover/leaf-ui';

import { Space, Text, Upload } from '@ifhover/leaf-ui';

const demoRequest = ({ signal, onProgress }: UploadRequest) =>
  new Promise<void>((resolve, reject) => {
    let percent = 0;
    const cleanup = () => {
      clearInterval(timer);
      signal.removeEventListener('abort', abort);
    };
    const abort = () => {
      cleanup();
      reject(new DOMException('Aborted', 'AbortError'));
    };
    const timer = setInterval(() => {
      percent += 12;
      onProgress(percent);
      if (percent >= 100) {
        cleanup();
        resolve();
      }
    }, 180);
    signal.addEventListener('abort', abort, { once: true });
    if (signal.aborted) abort();
  });

export function UploadDrag() {
  return (
    <Space direction="vertical" align="stretch">
      <Text tone="muted">
        This demo simulates progress locally. Use action or customRequest to connect your upload
        service.
      </Text>
      <Upload
        drag
        multiple
        accept="image/*"
        maxCount={4}
        customRequest={demoRequest}
        hint="Images only · cancellation and retry supported"
      />
    </Space>
  );
}

Usage notes

Without action or customRequest, files remain ready for business-managed submission. action uses XMLHttpRequest for progress: POST sends FormData, PUT sends the raw file. A JSON response may provide url and name; customRequest can return the same fields. Validation applies to chooser and drop; uploads can be cancelled, removed and retried. A custom request must observe signal. Removing files or unmounting cancels requests and releases local preview URLs. Server-side validation is still required.

Types

import type { UploadProps, UploadFile, UploadChangeInfo, UploadRejection, UploadRequest, UploadResult, FileItem } from '@ifhover/leaf-ui';

Manual upload and picture cards

capabilities.tsx
import { Button, Space, Upload, type UploadHandle } from '@ifhover/leaf-ui';

import { useRef } from 'react';

export function Capabilities() {
  const upload = useRef<UploadHandle>(null);
  return (
    <Space direction="vertical" align="stretch">
      <Upload
        ref={upload}
        autoUpload={false}
        concurrency={2}
        multiple
        accept="image/*"
        listType="picture-card"
        paste
        customRequest={async (request) => {
          await new Promise<void>((resolve, reject) => {
            const timer = setTimeout(resolve, 700);
            request.signal.addEventListener(
              'abort',
              () => {
                clearTimeout(timer);
                reject(request.signal.reason);
              },
              { once: true },
            );
          });
          return {};
        }}
      />
      <Space>
        <Button onClick={() => upload.current?.upload()}>
          {'Start upload'}
        </Button>
        <Button variant="outline" onClick={() => upload.current?.abort()}>
          {'Cancel uploads'}
        </Button>
      </Space>
    </Space>
  );
}

UploadProps API

PropertyTypeDefaultDescription
fileListreadonly UploadFile[][]Controlled / initial file list
defaultFileListreadonly UploadFile[][]Controlled / initial file list
onChange(files: UploadFile[], info: UploadChangeInfo) => void—File list and status changes
acceptstring—Accepted MIME types or extensions, e.g. image/*,.pdf
multiplebooleanfalseMultiple selection / disabled / dropzone
disabledbooleanfalseDisable interaction.
dragbooleanfalseMultiple selection / disabled / dropzone
maxCountnumber—Maximum total file count
maxSizenumber—Maximum bytes per file
beforeUpload(file: File) => boolean | File | Promise<boolean | File>—Validate before upload; false rejects, File transforms it
onReject(rejections: readonly UploadRejection[]) => void—Type, size, count or validation rejections
actionstring | ((file: File) => string | Promise<string>)—Upload URL; omitted configuration selects files only
method'POST' | 'PUT''POST'POST sends FormData; PUT sends the raw file
headersReadonly<Record<string, string>>—Request headers
dataReadonly<Record<string, string | Blob>>—Additional POST form data
withCredentialsbooleanfalseInclude credentials in cross-origin requests
fieldNamestring'file'File field name for POST
customRequest(request: UploadRequest) => Promise<UploadResult | void>—Custom async upload; use signal for cancellation
onRemove(file: UploadFile) => boolean | void | Promise<boolean> | Promise<void>—Before removal; return false to keep the file
showFileListbooleantrueDisplay the file list
hintReactNode—Hint / chooser button or dropzone title
childrenReactNode—Hint / chooser button or dropzone title
namestring—Field name in native FormData.
formstring—ID of the associated native form.
requiredboolean—Require a non-empty value; compound controls validate once.
autoUploadbooleantrueStart after validation; use ref.upload() when false.
concurrencynumber3Maximum concurrent upload requests.
directoryboolean—Choose a directory and its files, subject to browser support.
pasteboolean—Accept files from the clipboard.
listType'text' | 'picture' | 'picture-card''text'File list presentation.

UploadFile API

Extends FileItem and adds the fields below.

PropertyTypeDefaultDescription
uidstringRequiredRequired. Unique identifier
urlstring—Configure url.
namestring—Field name in native FormData.
sizenumber—Presentation size.
typestring—Configure type.
status'ready' | 'uploading' | 'done' | 'error' | 'cancelled'—Configure status.
percentnumber—Completion percentage.
errorstring—Configure error.
originFileglobalThis.File—Configure origin file.
responseunknown—Server response

UploadChangeInfo API

PropertyTypeDefaultDescription
fileUploadFile—File that changed
reason'add' | 'progress' | 'success' | 'error' | 'remove' | 'cancel'RequiredRequired. Change reason

UploadRejection API

PropertyTypeDefaultDescription
fileFileRequiredRequired. Rejected file
reason'type' | 'size' | 'count' | 'validation'RequiredRequired. Rejection reason
messagestringRequiredRequired. User-readable explanation

UploadRequest API

PropertyTypeDefaultDescription
fileFileRequiredRequired. File to upload
signalAbortSignalRequiredRequired. Cancellation signal, including removal or unmount
onProgress(percent: number) => voidRequiredRequired. Report upload progress

UploadResult API

PropertyTypeDefaultDescription
urlstring—Uploaded URL / name
namestring—Uploaded URL / name
responseunknown—Application response

FileItem API

PropertyTypeDefaultDescription
uidstring—Stable identifier, recommended
urlstring—File URL
namestring—Name, inferred from URL if omitted
sizenumber—Bytes; omitted sizes are not displayed
typestring—MIME type; inferred from the extension if omitted
status'ready' | 'uploading' | 'done' | 'error' | 'cancelled'—Optional upload status
percentnumber—Upload progress from 0 to 100
errorstring—Error description
originFileFile—Original browser File

UploadHandle

PropertyTypeDefaultDescription
upload(uid?: string) => void—Required. Start a specified upload or all pending files.
abort(uid?: string) => void—Required. Abort a specified upload or all uploads.
focus() => void—Required. Focus the control.