# File uploader: Virtual lists

> Pass virtual to the slotsmith FileUploader for batches of hundreds of files: only the rows in view are rendered, and the rest works as before.

For hundreds of files, pass `virtual` to the file uploader with the virtualizer hook as `use` (`useVirtualizer` from `@tanstack/react-virtual`): it renders only the rows in view inside a scroll area. `VirtualFileUploader` from `slotsmith/virtual` passes the hook for you. The queue, progress, retry and every slot work the same.

```tsx
import type { UploadItem } from "slotsmith/file-uploader";
import { VirtualFileUploader } from "slotsmith/virtual";
import { fakeUpload } from "../shared/fake-upload";

// 300 scans already stored, built from their index so every render gets the same ones.
const scans: UploadItem[] = Array.from({ length: 300 }, (_, index) => {
  const name = `scan-${String(index + 1).padStart(3, "0")}.pdf`;
  return {
    id: name,
    name,
    size: 40_000 + ((index * 7919) % 900_000),
    type: "application/pdf",
    status: "done",
    progress: 100,
    url: `/samples/uploaded/${name}`,
  };
});

export default function Virtual() {
  return (
    <VirtualFileUploader
      multiple
      maxFiles={500}
      defaultValue={scans}
      upload={fakeUpload}
      // A fallback row is 64.5px tall plus the 8px gap; the list scrolls inside 320px and renders only what is in view.
      virtual={{ estimateSize: 72.5, maxHeight: 320 }}
    />
  );
}
```

The list holds 300 stored scans and scrolls inside 320px; only about a dozen rows are in the page at a time. Add more files and they upload at the end of the list.

## Install

The hook comes from TanStack Virtual, an optional peer dependency that slotsmith never imports itself. Install it next to `slotsmith`:

```bash
npm i @tanstack/react-virtual
```

## The virtual option

The `virtual` prop takes the hook as `use` (`VirtualFileUploader` passes it for you), and three numbers:

- `estimateSize`: the height of one row in pixels, 56 by default. The rows are not measured, so set it to your row height plus the gap between rows: a fallback row is 64.5px and the gap 8px, so the demo uses 72.5.
- `maxHeight`: the height of the scroll area in pixels, 400 by default.
- `overscan`: how many rows to render beyond the visible area, 6 by default.

Rows out of view are replaced by two spacer `<li>` elements, hidden from assistive technology, that keep the scrollbar true to the whole list. The spacers never shrink and allow for the list's row gap, so the fallback flex list scrolls through every row, and so does a replacement `List` laid out as a grid or a block.

## Your own layout

The list on its own is `FileUploaderVirtualList`, also from `slotsmith/virtual`. Use it in place of `FileUploader.List` inside the [compound parts](https://slotsmith.dev/components/file-uploader/#reference), with the same options as props.
