# رافع الملفات: المنتقي

> Use the slotsmith FileUploader as a file picker: leave out upload, read the File objects from onFilesChange, and send them with your form.

Leave out the `upload` prop and the file uploader is a picker: nothing is sent, and `onFilesChange` hands you the picked `File` objects. Use it when the files go out with the rest of a form, in one request you write.

```tsx
import { useState, type FormEvent } from "react";
import { FileUploader, formatBytes } from "slotsmith/file-uploader";

export default function Picker() {
  // No `upload` prop: nothing is sent, and the value is plain File objects.
  const [files, setFiles] = useState<File[]>([]);
  const [sent, setSent] = useState<string | null>(null);

  const submit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    // The files go out with the rest of the form, in one request you control.
    const body = new FormData();
    for (const file of files) body.append("attachments", file);
    // A real form would now call: fetch("/api/reports", { method: "POST", body });
    void body;
    setSent(files.length ? files.map((file) => `${file.name} (${formatBytes(file.size)})`).join(", ") : "no files");
  };

  return (
    <form onSubmit={submit} style={{ display: "grid", gap: "0.75rem" }}>
      <FileUploader
        multiple
        accept=".csv,.xlsx,.pdf"
        maxFiles={3}
        onFilesChange={setFiles}
        labels={{ title: "Drop a spreadsheet or a PDF, or browse", hint: () => "CSV, Excel or PDF · up to 3 files · sent on submit" }}
      />
      <div>
        <button type="submit" style={{ background: "transparent" }}>
          Send the form
        </button>
      </div>
      <p>{sent === null ? "Nothing sent yet." : `The form would send: ${sent}.`}</p>
    </form>
  );
}
```

Pick or drop two files, then send the form: the line under it lists what the request would carry. Nothing is uploaded.

## What a picker does

Without [`upload`](https://slotsmith.dev/ar/components/file-uploader/api/#prop-upload) every item stays `ready`, and the item shows its name and size but no status. Everything else works as in an uploader: the drop zone, the [constraints](https://slotsmith.dev/ar/components/file-uploader/guides/validation/), the image previews and the Remove button.

- [`onFilesChange`](https://slotsmith.dev/ar/components/file-uploader/api/#prop-onFilesChange) is called with every picked file after each change, removals included.
- [`onValueChange`](https://slotsmith.dev/ar/components/file-uploader/api/#prop-onValueChange) gives the full items instead, and `getFiles(items)` turns them into files.
- [`multiple`](https://slotsmith.dev/ar/components/file-uploader/api/#prop-multiple) is off by default, like the native input: a second pick replaces the first file.

## Sending the files

Build the request from the files you were handed: append each one to a `FormData`, as the demo does in its submit handler, and send it with `fetch` or your form library.

In a plain HTML form, give the uploader a [`name`](https://slotsmith.dev/ar/components/file-uploader/api/#props). The form then sends every item under that name. A file picked or dropped in the browser goes as the file, as `<input type="file" name>` would send it. A file already on a server, shown from a URL in an [edit form](https://slotsmith.dev/ar/components/file-uploader/guides/editing-a-record/) or uploaded by `upload`, goes as its URL, so the server can tell the files to keep from the new ones. `new FormData(form).getAll(name)` returns both, and a form posted without script sends them as `multipart/form-data` (set `encType` on the form).

```tsx
import { useState, type FormEvent } from "react";
import { FileUploader } from "slotsmith/file-uploader";

export default function Form() {
  const [sent, setSent] = useState<string[] | null>(null);

  // A plain form: the uploader's hidden input sends the files under "attachments".
  const submit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const files = new FormData(event.currentTarget).getAll("attachments") as File[];
    setSent(files.map((file) => file.name));
    // A real form would post here: fetch("/api/messages", { method: "POST", body: new FormData(event.currentTarget) }).
  };

  return (
    <form onSubmit={submit} style={{ display: "grid", gap: "0.75rem", maxWidth: 420 }}>
      <FileUploader name="attachments" multiple maxFiles={3} />
      <button type="submit" style={{ justifySelf: "start", background: "transparent" }}>
        Send
      </button>
      <p>{sent === null ? "Nothing sent yet." : `Sent: ${sent.join(", ") || "no files"}`}</p>
    </form>
  );
}
```

Pick or drop files and press Send: the form's own `FormData` lists them.

## Custom text

The demo replaces two [labels](https://slotsmith.dev/ar/components/file-uploader/api/#labels): the zone's [`title`](https://slotsmith.dev/ar/components/file-uploader/api/#label-title), and the [`hint`](https://slotsmith.dev/ar/components/file-uploader/api/#label-hint) under it. The hint is a function that receives the limits (`accept`, `maxSize`, `maxFiles`) already formatted; the default joins them, as in "image/\*, .pdf · up to 5 MB".
