Forms

Upload

A file queue with a trigger, a list, per-file progress and retry — plus `Upload.Dragger`, the drop-target form. The queue arithmetic is framework-free and lives in `@crosskit-ui/core`, so the `accept` filter applies to a drop as well as to the file dialog; the attribute alone only filters the OS dialog.

data-scope="upload"

What you get for free

  • Upload.Dragger, with a drag counter that does not flicker over child elements
  • Progress as an inline custom property, so the bar grows from the inline start in RTL too
  • Retry on a failed file, which is the same transition as a first attempt
  • A request that resolves after its file was removed cannot put the row back
  • customRequest, for a transport that is not a plain multipart POST

Usage

import { Upload, Button } from "@crosskit-ui/react";

<Upload action="/api/upload" multiple accept="image/*" maxCount={5}>
  <Button>Select files</Button>
</Upload>

<Upload.Dragger action="/api/upload" listType="picture">
  <p>Drop files here</p>
</Upload.Dragger>

{/* Inside a Form. Form.Item's default getValueFromEvent returns the first
    argument whole, and ours is the change info rather than a value — so the
    binding has to say which half of it is the value. */}
<Form.Item name="files" valuePropName="fileList" getValueFromEvent={info => info.fileList}>
  <Upload action="/api/upload">
    <Button>Select files</Button>
  </Upload>
</Form.Item>
<Upload action="/api/upload" multiple accept="image/*" :max-count="5">
  <Button>Select files</Button>
</Upload>
<Upload action="/api/upload" multiple accept="image/*" maxCount={5}>
  <Button>Select files</Button>
</Upload>
<ck-upload action="/api/upload" multiple accept="image/*" [maxCount]="5">
  <button ckButton>Select files</button>
</ck-upload>

Props

PropTypeDefaultNotes
fileList, defaultFileListUploadFile[]The queue, controlled or not. Entries carry uid, name, status and percent.
onChange(info: { file: UploadFile; fileList: UploadFile[] }) => voidFires on add, on every progress tick, on settle and on remove. A controlled Upload needs it to move at all.
actionstring | ((file: File) => string | Promise<string>)Where to POST. A function is awaited after the row is already showing as uploading, which is what signed-URL flows need.
method"POST" | "PUT""POST"Request method.
headersRecord<string, string>Extra request headers.
dataRecord<string, string> | ((file: File) => Record<string, string>)Extra multipart fields sent alongside the file.
namestring"file"The multipart FIELD name — not the form field name `name` means on Input, Select and DatePicker. Kept for drop-in compatibility; it is a genuine collision.
withCredentialsbooleanfalseSend cookies cross-origin.
customRequest(args: UploadRequestArgs) => { abort: () => void } | voidReplaces the built-in transport entirely. Wins over `action`. Return an abort and remove/unmount will call it.
beforeUpload(file: File, files: File[]) => boolean | Promise<boolean>`false` — or a rejected promise — lists the file at status `selected` and never uploads it. The second argument is the files that were ADMITTED, not the raw batch: what `accept` and `maxCount` let through.
multiplebooleanfalseAllow a multi-pick.
acceptstringThe `<input accept>` grammar. Re-checked on drop as well, which the attribute cannot do.
maxCountnumber`1` REPLACES the list and drops `multiple` from the dialog; any other value truncates the incoming batch to the room left. The replaced row is aborted, revoked and reported through `onChange` — a replacement is a removal plus an addition.
disabledbooleanfalseRefuse picks and drops, and disable each row's own Retry and Remove.
listType"text" | "picture""text"`picture` shows a thumbnail, minted and revoked by the component.
showUploadListbooleantrueRender the list at all. Nothing is emitted when it is off or the list is empty.
onRemove(file: UploadFile) => boolean | Promise<boolean>`false` vetoes the removal. Removing aborts an in-flight request first.
onPreview(file: UploadFile) => voidMakes the file name a button rather than a link.
directorybooleanfalsePick a whole folder. Implies `multiple`, and names rows by their folder path.

Prop names are identical in all four frameworks. Only two-way binding differs, and each framework uses its own idiom over the same underlying prop.

Parts

data-partDescription
rootThe wrapper. Carries data-list-type and data-disabled.
triggerWraps your children on the plain Upload. Inert — the child is the control.
dropzoneUpload.Dragger's target. role=button, with Enter and Space.
inputThe hidden file input. Visually hidden, never display:none.
listThe <ul>. Absent when there is nothing in it.
itemOne row. data-state is selected | uploading | done | error.
item-nameA button with onPreview, a link with a url, plain text otherwise.
item-progressrole=progressbar; the fill reads --ck-upload-progress.
item-actionsRetry and remove, at the inline end.
statusA visually hidden live region, written only on settle.