Upload Field
Form field whose value is the object keys of the files uploaded through it.
UploadField turns uploads into a form value. Each file that uploads adds
its object key, and every key submits as a hidden <input name>. The upload
parts go inside it, as they would inside Upload.
Preview
Upload up to three files, then submit the form to see the keys it sends.
Install
Complete UI Setup first: the stylesheet and one
<Toaster />. Then add this component:
import { UploadField, UploadFieldValue } from "@dimah-s3/ui";Usage
"use client";
import { useUpload } from "@dimah-s3/react";
import {
UploadDropzone,
UploadField,
UploadFieldValue,
UploadToasts,
} from "@dimah-s3/ui";
export function TicketForm({
save,
}: {
save: (formData: FormData) => void | Promise<void>;
}) {
const upload = useUpload({ route: "attachments", maxFiles: 5 });
return (
<form action={save} className="space-y-4">
<UploadField upload={upload} name="attachments" maxFiles={5}>
<UploadDropzone />
<UploadFieldValue />
</UploadField>
<UploadToasts upload={upload} />
<button type="submit">Send</button>
</form>
);
}The dropzone shows the files in flight inside it. Once a file uploads it
moves to UploadFieldValue, which lists the keys the field holds, so a file
never shows twice. On the server, read the keys with formData.getAll("attachments").
maxFiles is a client-side limit, so check the keys and their count in your
own endpoint. See Forms for the rest of the flow.
Examples
Attach button
A message form with an attach button, a key saved earlier as
defaultValue, and UploadStatus for the files in flight. Remove the saved
file or attach two more to fill the field.
Value
- A file adds its key once it uploads. Files that fail, are cancelled, or are still in flight add nothing.
- The value grows across calls, even though the hook replaces its queue.
- Files that uploaded before the field mounted are left out.
UploadFieldValuelists the keys, each with aRemove {name}action and a thumbnail for images the field uploaded. Removing a key does not delete the object.- Inside the field,
UploadStatusandUploadFileListleave out files that uploaded: they show only files in flight or failed. The field setshideUploadedon itsUploadroot for that.
Keep the value yourself with value and onValueChange, or start from
defaultValue. onValueChange runs after the change commits. Keys the field
did not upload show the last segment of the key as their name.
const [keys, setKeys] = useState<string[]>(post.attachmentKeys);
<UploadField
upload={upload}
name="attachments"
value={keys}
onValueChange={setKeys}
>
<UploadTrigger />
<UploadFieldValue />
</UploadField>;Limit
With maxFiles, each call takes only the files that still fit: maxFiles
minus the keys in the value and the files still uploading. The hook rejects
the rest with TOO_MANY_FILES.
Once the field is full, its UploadTrigger and UploadDropzoneTrigger
stay focusable but are aria-disabled, and the picker, drops, and pastes do
nothing. "Maximum files reached." leads the dropzone trigger's description. The
field passes isFull to its Upload root, so your own parts can read
useUploadContext().isFull:
function FullHint() {
const { isFull } = useUploadContext();
return isFull ? <p>Remove a file to add another.</p> : null;
}Form reset
Without value, the field restores defaultValue when its form resets,
including React 19's automatic reset after a <form action>, and reports
it through onValueChange. Call upload.reset() to clear the status list
too. A controlled field must reset value itself.
Custom parts
useUploadFieldContext() returns name, value, items (the keys with
the file name, size, and type the field knows), remaining, and remove.
Pass a function to UploadFieldValue to render each item yourself:
function AttachmentNames() {
const { remove } = useUploadFieldContext();
return (
<UploadFieldValue>
{(item) => (
<span>
{item.fileName}
<button type="button" onClick={() => remove(item.key)}>
Remove
</button>
</span>
)}
</UploadFieldValue>
);
}Render custom parts inside UploadField.
Props
import type {
UploadFieldProps,
UploadFieldValueProps,
UploadFieldContextValue,
} from "@dimah-s3/ui";UploadField
Prop
Type
UploadFieldValue
Prop
Type
useUploadFieldContext
Prop
Type