2.0 beta — new releases can include breaking changes. Stable 1.x docs
dimah-s3v2.0.0-beta.0
ComponentsUpload

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.

Drag and drop files here

You can upload 3 files. Each up to 75.0 MB.

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.

  • invoice-0042.pdf

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.
  • UploadFieldValue lists the keys, each with a Remove {name} action and a thumbnail for images the field uploaded. Removing a key does not delete the object.
  • Inside the field, UploadStatus and UploadFileList leave out files that uploaded: they show only files in flight or failed. The field sets hideUploaded on its Upload root 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

On this page