# Preview files

A keyboard-accessible file viewer shared by every family that shows files: a file browser, an upload queue, chat attachments. It takes a list of preview items (id, name, media type, nullable url), opens a modal on one, steps between them and draws each with a native stage: image with zoom, audio, video, a sandboxed frame for PDF, highlighted text, CSV. A format it cannot draw gets an honest fallback, never an endless spinner.

Import: @slices.sh/quick-look/headless

Public parts: 39

## Installation

### 1. Install the service registry

services/index.ts, which `import "@/services"` loads. A provider setup writes the same file; skip this if your app has one.

```bash
npx shadcn@latest add "https://slices.sh/r/services.json"
```

### 2. Install the headless library

```bash
npm install @slices.sh/quick-look@^0.1.2
```

### 3. Install the service

Install the provider configuration via shadcn or the slices CLI. Skip this step if your app already has the service configured. Provider commands and provisioning: https://slices.sh/docs/services/quick-look.md

### Optional: XLSX document stage

```bash
npm install @slices.sh/file-storage
```

## Anatomy

### Composition

```text
Preview files documentation grouping · choose one source
  QuickLook.Root items={files} · standalone composition
    QuickLook.Trigger itemId="lake"
    QuickLook.Content
      QuickLook.Title
      QuickLook.Description
      QuickLook.Close
      QuickLook.Selected
        QuickLook.StageRoot item={item}
          FileDocument.Root
            FileDocument.Loading
            FileDocument.Failed
            FileDocument.Formats formats={["docx"]}
              FileDocument.Content
                FileDocument.Boundary
                  DocumentDocx bytes={bytes}
            FileDocument.Formats formats={["pdf"]}
              FileDocument.Content
                FileDocument.Boundary
                  DocumentPdf bytes={bytes}
            FileDocument.Formats formats={["xlsx"]}
              FileDocument.Content
                FileDocument.Boundary
                  DocumentXlsx bytes={bytes} name="Budget.xlsx"
            FileDocument.Formats formats={["pptx"]}
              FileDocument.Content
                FileDocument.Boundary
                  DocumentPptx bytes={bytes}
          QuickLook.Frame
          QuickLook.Csv
          QuickLook.Text kind="code"
          QuickLook.Loading
          QuickLook.ZoomIn
          QuickLook.ZoomOut
          QuickLook.Fit
          QuickLook.Image
          QuickLook.Audio
          QuickLook.Video
          QuickLook.Unavailable
          QuickLook.Link
      QuickLook.Previous
      QuickLook.Next
      QuickLook.Page
  Files.Container bucket="files" · alternative source
    FilePreview.Root creates QuickLook.Root internally
      Files.Entries
        Files.Entry
          FilePreview.Open
          FilePreview.Trigger
      FilePreview.Download
  Upload.Root alternative source
    FilePreview.UploadsRoot creates QuickLook.Root internally
      Upload.Uploads
        Upload.Row
          FilePreview.UploadTrigger
```

## Preview your files

```tsx
"use client"

import { QuickLook } from "@slices.sh/quick-look/headless"
import type { PreviewItem } from "@slices.sh/quick-look/services/types"

export function Example({ items }: { items: PreviewItem[] }) {
  return (
    <QuickLook.Root items={items}>
      {items.map((item) => (
        <QuickLook.Trigger key={item.id} itemId={item.id}>
          {item.name}
        </QuickLook.Trigger>
      ))}
      <QuickLook.Content>
        <QuickLook.Title>File preview</QuickLook.Title>
        <QuickLook.Selected>
          {({ item }) => (
            <QuickLook.StageRoot item={item}>
              <QuickLook.Image />
              <QuickLook.Unavailable>
                No preview available.
              </QuickLook.Unavailable>
            </QuickLook.StageRoot>
          )}
        </QuickLook.Selected>
        <QuickLook.Close>Close</QuickLook.Close>
      </QuickLook.Content>
    </QuickLook.Root>
  )
}

```

Supply items with id, name, mediaType and url. The dialog needs no storage service.

## Your own element

```tsx
"use client"

import { QuickLook } from "@slices.sh/quick-look/headless"
import type { PreviewItem } from "@slices.sh/quick-look/services/types"

export function Example({ items }: { items: PreviewItem[] }) {
  return (
    <QuickLook.Root items={items}>
      {items.map((item) => (
        <QuickLook.Trigger key={item.id} itemId={item.id}>
          {item.name}
        </QuickLook.Trigger>
      ))}
      <QuickLook.Content
        render={
          <div className="fixed inset-8 rounded-xl border bg-white p-6" />
        }
      >
        <QuickLook.Title>File preview</QuickLook.Title>
        <QuickLook.Selected>
          {({ item }) => (
            <QuickLook.StageRoot item={item}>
              <QuickLook.Image />
              <QuickLook.Unavailable>
                No preview available.
              </QuickLook.Unavailable>
            </QuickLook.StageRoot>
          )}
        </QuickLook.Selected>
        <QuickLook.Close>Close</QuickLook.Close>
      </QuickLook.Content>
    </QuickLook.Root>
  )
}

```

The same behavior, with your element and your styles. All other parts stay unchanged.

## Complete API reference

### QuickLook.Root

scope · Generic

The selection, and a trigger per item (itemId) that opens the viewer on it.

Import: @slices.sh/quick-look/headless

```tsx
QuickLookRootProps

type QuickLookRootProps = RootProps & {
  items: readonly PreviewItem[]
  /**
   * Quick look has no service to be missing, so nothing here throws. `false` is off: the root
   * renders nothing, and so does every part of it. Anything else leaves it on.
   */
  enabled?: Enabled
  /** Accepted so every root takes the same props: quick look has no configs. */
  config?: string
}
```

Source: @slices.sh/quick-look/src/headless/root.tsx

### QuickLook.Content

content · Generic · Element: div

The modal content with its motion surface, its title and description for assistive technology, and closing it. Focus, Escape and dismissal are the dialog primitive's.

Import: @slices.sh/quick-look/headless

QuickLook.Content requires QuickLook.Root.

```tsx
PartProps<"div">
```

Source: @slices.sh/quick-look/src/headless/content.tsx

### QuickLook.Title

content · Generic · Element: h2

The modal content with its motion surface, its title and description for assistive technology, and closing it. Focus, Escape and dismissal are the dialog primitive's.

Import: @slices.sh/quick-look/headless

```tsx
PartProps<"h2">
```

Source: @slices.sh/quick-look/src/headless/title.tsx

### QuickLook.Description

content · Generic · Element: p

The modal content with its motion surface, its title and description for assistive technology, and closing it. Focus, Escape and dismissal are the dialog primitive's.

Import: @slices.sh/quick-look/headless

```tsx
PartProps<"p">
```

Source: @slices.sh/quick-look/src/headless/description.tsx

### QuickLook.Close

action · Generic · Element: button

The modal content with its motion surface, its title and description for assistive technology, and closing it. Focus, Escape and dismissal are the dialog primitive's.

Import: @slices.sh/quick-look/headless

```tsx
PartProps<"button">
```

Source: @slices.sh/quick-look/src/headless/close.tsx

### QuickLook.Selected

gate · Generic

Selected renders with { item } while a file is open. StageRoot takes that item (and text, pdf to allow those reads) and resets its image, error and load state by file identity; keep it mounted while the item changes so the navigation buttons keep focus.

Import: @slices.sh/quick-look/headless

QuickLook.Selected requires QuickLook.Root.

```tsx
GateProps<{ item: PreviewItem }>
```

Source: @slices.sh/quick-look/src/headless/selected.tsx

### QuickLook.StageRoot

scope · Generic

Reset per-file state without replacing focused navigation controls.

Import: @slices.sh/quick-look/headless

```tsx
RootProps & { item: PreviewItem; text?: boolean; pdf?: boolean }
```

Source: @slices.sh/quick-look/src/headless/stage-root.tsx

### QuickLook.Previous

action · Generic · Element: button

Previous and next through the items, disabled at the ends, and the page with { index, total }.

Import: @slices.sh/quick-look/headless

QuickLook.Previous requires QuickLook.Root.

```tsx
PartProps<"button">
```

Source: @slices.sh/quick-look/src/headless/previous.tsx

### QuickLook.Page

content · Generic · Element: span

Previous and next through the items, disabled at the ends, and the page with { index, total }.

Import: @slices.sh/quick-look/headless

QuickLook.Page requires QuickLook.Root.

```tsx
PartProps<"span", { index: number; total: number }>
```

Source: @slices.sh/quick-look/src/headless/page.tsx

### QuickLook.Next

action · Generic · Element: button

Previous and next through the items, disabled at the ends, and the page with { index, total }.

Import: @slices.sh/quick-look/headless

QuickLook.Next requires QuickLook.Root.

```tsx
PartProps<"button">
```

Source: @slices.sh/quick-look/src/headless/next.tsx

### QuickLook.ZoomOut

action · Generic · Element: button

Zoom in, zoom out and fit, with Fit giving { percent }. They render only on an image stage.

Import: @slices.sh/quick-look/headless

QuickLook.ZoomOut requires QuickLook.StageRoot.

```tsx
PartProps<"button">
```

Source: @slices.sh/quick-look/src/headless/zoom-out.tsx

### QuickLook.Fit

action · Generic · Element: button

Zoom in, zoom out and fit, with Fit giving { percent }. They render only on an image stage.

Import: @slices.sh/quick-look/headless

QuickLook.Fit requires QuickLook.StageRoot.

```tsx
PartProps<"button", { percent: number }>
```

Source: @slices.sh/quick-look/src/headless/fit.tsx

### QuickLook.ZoomIn

action · Generic · Element: button

Zoom in, zoom out and fit, with Fit giving { percent }. They render only on an image stage.

Import: @slices.sh/quick-look/headless

QuickLook.ZoomIn requires QuickLook.StageRoot.

```tsx
PartProps<"button">
```

Source: @slices.sh/quick-look/src/headless/zoom-in.tsx

### QuickLook.Image

content · Generic · Element: img

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Image requires QuickLook.StageRoot.

```tsx
PartProps<"img">
```

Source: @slices.sh/quick-look/src/headless/image.tsx

### QuickLook.Link

content · Generic · Element: a

Loading while text is being read; Unavailable when there is no address, the kind cannot be drawn, or the load failed; and Link, which opens the file in a new tab only for an http(s) or same-origin address.

Import: @slices.sh/quick-look/headless

QuickLook.Link requires QuickLook.StageRoot.

```tsx
PartProps<"a">
```

Source: @slices.sh/quick-look/src/headless/link.tsx

### QuickLook.Trigger

action · Generic · Element: button

The selection, and a trigger per item (itemId) that opens the viewer on it.

Import: @slices.sh/quick-look/headless

QuickLook.Trigger requires QuickLook.Root.

```tsx
PartProps<"button"> & { itemId: string }
```

Source: @slices.sh/quick-look/src/headless/trigger.tsx

### QuickLook.Frame

content · Generic · Element: iframe

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Frame requires QuickLook.StageRoot.

```tsx
PartProps<"iframe">
```

Source: @slices.sh/quick-look/src/headless/frame.tsx

### QuickLook.Audio

content · Generic · Element: audio

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Audio requires QuickLook.StageRoot.

```tsx
PartProps<"audio">
```

Source: @slices.sh/quick-look/src/headless/audio.tsx

### QuickLook.Video

content · Generic · Element: video

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Video requires QuickLook.StageRoot.

```tsx
PartProps<"video">
```

Source: @slices.sh/quick-look/src/headless/video.tsx

### QuickLook.Csv

gate · Generic

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Csv requires QuickLook.StageRoot.

```tsx
GateProps<CsvPreview>
```

Source: @slices.sh/quick-look/src/headless/csv.tsx

### QuickLook.Text

gate · Generic

Each renders only for its kind of file once its address is known: an image, audio and video with native controls, a sandboxed frame for a PDF, text as code or markdown (Text kind), and the first rows of a CSV with whether they were cut short.

Import: @slices.sh/quick-look/headless

Capability: previews

QuickLook.Text requires QuickLook.StageRoot.

```tsx
GateProps<{ text: string; name: string; mediaType: string }> & { kind: "code" | "markdown" | "csv" }
```

Source: @slices.sh/quick-look/src/headless/text.tsx

### QuickLook.Loading

gate · Generic

Loading while text is being read; Unavailable when there is no address, the kind cannot be drawn, or the load failed; and Link, which opens the file in a new tab only for an http(s) or same-origin address.

Import: @slices.sh/quick-look/headless

QuickLook.Loading requires QuickLook.StageRoot.

```tsx
GateProps
```

Source: @slices.sh/quick-look/src/headless/loading.tsx

### QuickLook.Unavailable

gate · Generic

Loading while text is being read; Unavailable when there is no address, the kind cannot be drawn, or the load failed; and Link, which opens the file in a new tab only for an http(s) or same-origin address.

Import: @slices.sh/quick-look/headless

QuickLook.Unavailable requires QuickLook.StageRoot.

```tsx
GateProps
```

Source: @slices.sh/quick-look/src/headless/unavailable.tsx

### FilePreview.Root

scope

Root for a folder's rows (what is on screen first, then what a tree or column has read). UploadsRoot, inside Upload.Root, for the files in a queue, with local previews until they have landed.

Import: @slices.sh/file-storage/headless/file-preview

```tsx
RootProps
```

Source: @slices.sh/file-storage/src/headless/file-preview/root.tsx

### FilePreview.Open

action · Element: button

Open behaves as Files.Open on a folder and as a viewer trigger on a file. Trigger renders only for a file, for an embedded list that should not navigate folders. UploadTrigger opens a queued file, and is disabled until there is a stored record or a local preview.

Import: @slices.sh/file-storage/headless/file-preview

Capability: previews

FilePreview.Open requires Files.Entries.

```tsx
PartProps<"button">
```

Source: @slices.sh/file-storage/src/headless/file-preview/open.tsx

### FilePreview.Trigger

action · Element: button

File-only trigger, so an embedded list need not invent folder navigation.

Import: @slices.sh/file-storage/headless/file-preview

Capability: previews

FilePreview.Trigger requires Files.Entries.

```tsx
PartProps<"button">
```

Source: @slices.sh/file-storage/src/headless/file-preview/trigger.tsx

### FilePreview.Download

content · Element: a

A link to the file in view, signed for saving with its own name, or the local copy of a file not yet uploaded. data-pending until the address arrives; nothing while no file is open.

Import: @slices.sh/file-storage/headless/file-preview

FilePreview.Download requires a preview root.

```tsx
PartProps<"a">
```

Source: @slices.sh/file-storage/src/headless/file-preview/download.tsx

### FilePreview.UploadsRoot

scope

Root for a folder's rows (what is on screen first, then what a tree or column has read). UploadsRoot, inside Upload.Root, for the files in a queue, with local previews until they have landed.

Import: @slices.sh/file-storage/headless/file-preview

FilePreview.UploadsRoot requires Upload.Root.

```tsx
RootProps
```

Source: @slices.sh/file-storage/src/headless/file-preview/uploads-root.tsx

### FilePreview.UploadTrigger

action · Element: button

Open behaves as Files.Open on a folder and as a viewer trigger on a file. Trigger renders only for a file, for an embedded list that should not navigate folders. UploadTrigger opens a queued file, and is disabled until there is a stored record or a local preview.

Import: @slices.sh/file-storage/headless/file-preview

Capability: previews

FilePreview.UploadTrigger requires Upload.Uploads.

```tsx
PartProps<"button">
```

Source: @slices.sh/file-storage/src/headless/file-preview/upload-trigger.tsx

### FileDocument.Root

scope

Root holds the read. Boundary is an error boundary around a lazy viewer: a chunk that fails to load or a parser that throws renders fallback, and resetKey (the bytes) clears it for the next file.

Import: @slices.sh/file-storage/headless/file-document

FileDocument.Root requires QuickLook.StageRoot.

```tsx
RootProps
```

Source: @slices.sh/file-storage/src/headless/file-document/root.tsx

### FileDocument.Formats

gate

Renders its children when the stage's file is one of formats ("pdf", "docx", "xlsx", "pptx"), or with except, when it is none of them. A file of unknown format is never one of them.

Import: @slices.sh/file-storage/headless/file-document

Capability: documents (the formats installed)

FileDocument.Formats requires QuickLook.StageRoot.

```tsx
GateProps & { formats?: readonly DocumentFormat[]; except?: boolean }
```

Source: @slices.sh/file-storage/src/headless/file-document/formats.tsx

### FileDocument.Content

gate

Loading while the bytes are on their way, Failed when the read was refused or exceeded its bound, and Content with { bytes, name } once they have arrived.

Import: @slices.sh/file-storage/headless/file-document

Capability: previews

FileDocument.Content requires FileDocument.Root.

FileDocument.Content requires QuickLook.StageRoot.

```tsx
GateProps<{ bytes: ArrayBuffer; name: string }>
```

Source: @slices.sh/file-storage/src/headless/file-document/content.tsx

### DocumentDocx

content · Generic · Element: div

Visual renderer — not purely headless.

DocumentDocx takes bytes (an ArrayBuffer), loading and failed (ReactNodes it shows while parsing and when the file cannot be drawn). It stays read-only: nothing is written back.

Import: @slices.sh/file-storage/headless/document-docx

```tsx
PartProps<"div"> & DocxDocumentProps
```

Source: @slices.sh/file-storage/src/headless/document-docx/index.tsx

### DocumentPdf

content · Element: div

Visual renderer — not purely headless.

DocumentPdf takes bytes (an ArrayBuffer), loading and failed (ReactNodes it shows while parsing and when the file cannot be drawn). It stays read-only: nothing is written back.

Import: @slices.sh/file-storage/headless/document-pdf

Capability: assets (the versioned viewer files)

```tsx
PartProps<"div"> & Omit<PdfDocumentProps, "wasmUrl">
```

Source: @slices.sh/file-storage/src/headless/document-pdf/index.tsx

### DocumentPptx

content · Element: div

Visual renderer — not purely headless.

DocumentPptx takes bytes (an ArrayBuffer), loading and failed (ReactNodes it shows while parsing and when the file cannot be drawn). It stays read-only: nothing is written back.

Import: @slices.sh/file-storage/headless/document-pptx

Capability: assets (the versioned viewer files)

```tsx
PartProps<"div"> & Omit<PptxDocumentProps, "wasmUrl" | "workerUrl">
```

Source: @slices.sh/file-storage/src/headless/document-pptx/index.tsx

### DocumentXlsx

content · Generic · Element: div

Visual renderer — not purely headless.

DocumentXlsx takes bytes (an ArrayBuffer), name (the file name, used for sheet titles), loading and failed (ReactNodes it shows while parsing and when the file cannot be drawn). It stays read-only: nothing is written back.

Import: @slices.sh/file-storage/headless/document-xlsx

```tsx
PartProps<"div"> & XlsxDocumentProps
```

Source: @slices.sh/file-storage/src/headless/document-xlsx/index.tsx

### FileDocument.Loading

gate

Loading while the bytes are on their way, Failed when the read was refused or exceeded its bound, and Content with { bytes, name } once they have arrived.

Import: @slices.sh/file-storage/headless/file-document

Capability: previews

FileDocument.Loading requires FileDocument.Root.

```tsx
GateProps
```

Source: @slices.sh/file-storage/src/headless/file-document/loading.tsx

### FileDocument.Failed

gate

Loading while the bytes are on their way, Failed when the read was refused or exceeded its bound, and Content with { bytes, name } once they have arrived.

Import: @slices.sh/file-storage/headless/file-document

Capability: previews

FileDocument.Failed requires FileDocument.Root.

```tsx
GateProps
```

Source: @slices.sh/file-storage/src/headless/file-document/failed.tsx

### FileDocument.Boundary

content

A failed lazy chunk or parser does not take down the surrounding file browser.

Import: @slices.sh/file-storage/headless/file-document

```tsx
{ children?: ReactNode; fallback: ReactNode; resetKey?: unknown }
```

Source: @slices.sh/file-storage/src/headless/file-document/boundary.tsx

### Files.Container

scope

The one place a file explorer talks to the file service.

It maps the client onto what the screen below draws: the folder that is open and its pages
(`Files.Root`), the folders read besides it, from the store's listings or from
`loadChildren`, and the address of every file and every picture, which the parts ask the
client for and the server signs. What the screen may do is read here too, from what the
client has: a part that needs `upload` asks `Files.CanUpload`, so a service with none gets a
browser with no upload button and no drop, and not a button that fails when pressed.

The screen never calls the service itself: pattern code calls no hook, and every part under
here reaches the service through this container's contexts. → docs/rules/patterns-read-no-state.md

Import: @slices.sh/file-storage/headless

```tsx
ContainerProps

type ContainerProps = RootProps &
  Omit<FilesRootProps, "children"> & {
    /** Where a folder's children come from when it is not the store's own listing. */
    loadChildren?: LoadChildren
  }
```

Source: @slices.sh/file-storage/src/headless/container.tsx

### Files.Entries

scope

What the browser is showing: the directory's children, or what the search found.

Under a folder that is open, in a tree, its entries follow it with how far in they are.

One part for both, because a screen that drew them separately would be two lists whose
rows, selection and actions drifted apart — and a search that showed a different kind of
row than the folder it searched is the thing people report as a bug.

Where the screen draws uploads in place, each one that is on its way is a row among these, in
the order of the rest, with `Files.Pending` to say how far it has got. A row that is not
there yet cannot be opened, picked, renamed or moved: its parts say so themselves.

Import: @slices.sh/file-storage/headless

```tsx
RootProps
```

Source: @slices.sh/file-storage/src/headless/entries.tsx

### Files.Entry

content · Element: li

The gate around everything a listed folder draws, the repeater of rows on screen, the row, the next page, roving keys over the rows, a windowed list for long folders and the live announcement of what the folder now holds.

Import: @slices.sh/file-storage/headless

Files.Entry must be inside <Files.Entries>.

Files.Entry must be inside <Files.Root>.

```tsx
PartProps<"li", EntryState>

type EntryState = { kind: "file" | "directory"; selected: boolean }
```

Source: @slices.sh/file-storage/src/headless/entry.tsx

### Upload.Root

scope

The queue, its limits, and its field registration when it has a name inside a Composer.

Import: @slices.sh/file-storage/headless

```tsx
UploadRootProps

type UploadRootProps = RootProps & {
  /**
   * The key of this value in a Composer's message. With a name, inside a Composer, the uploader
   * is a field: its value is the finished files, and it holds the submit while one uploads.
   * Without one, or outside a Composer, it is a plain uploader.
   */
  name?: string
  /**
   * Left out or `true`: required, and a missing service throws. `"optional"`: renders its
   * children and every upload part does nothing, which is what a pattern's paperclip passes.
   * `false`: off. A Composer whose sink takes no files turns it off too.
   */
  enabled?: Enabled
  /** The file-storage config, `"default"` when left out. */
  config?: string
  bucket?: string
  /** The directory files land in. */
  path?: string
  /**
   * Bytes. A larger file is turned away before anything is sent. The server's own limit, in
   * `services/file-storage/limits.ts`, applies too: the lower of the two is the one enforced.
   */
  maxSize?: number
  maxFiles?: number
  /** An `accept` attribute: `"image/*,.pdf"`. Offered to the picker and checked on the way in. */
  accept?: string
  /**
   * Files from 5 MiB go up in resumable parts wherever the backend has them, so a large file
   * survives a dropped connection and can be paused. On unless turned off: a screen that had
   * to opt in sent a 2 GB file as one request. A backend without parts is sent whole.
   */
  multipart?: boolean
  /**
   * Hidden files inside a folder (`.DS_Store`, `.git/…`, `Thumbs.db`) are left out of what a
   * folder drop or folder pick brings in. Say `includeHidden` to send them. A hidden file
   * chosen on its own is always taken.
   */
  includeHidden?: boolean
  /** What a name that is taken does: replace, keep both, refuse, or `"ask"` and wait to be told. Left out, replace. */
  ifExists?: IfExists | "ask"
  /** Each file as it lands, with the permalink to keep for it. */
  onUploaded?(uploaded: Uploaded): void
  /** Where a message and its finished attachments go. Without it there is nowhere to send. */
  onSend?(message: { text: string; files: FileRecord[] }): void
}
```

Source: @slices.sh/file-storage/src/headless/upload-root.tsx

### Upload.Uploads

scope

Every upload in the queue, each in its own scope.

Import: @slices.sh/file-storage/headless

File.Uploads must be inside <File.Upload>.

```tsx
RootProps
```

Source: @slices.sh/file-storage/src/headless/uploads.tsx

### Upload.Row

content · Element: li

One upload's row. `data-state` is where it is, which is what a skin colours by, and
`data-kind` what it is, which is what a skin draws an icon for.

Import: @slices.sh/file-storage/headless

File.UploadRow must be inside <File.Uploads>.

```tsx
PartProps<"li", { state: UploadState }>
```

Source: @slices.sh/file-storage/src/headless/upload-row.tsx
