# Prompt Input Attachments

Prompt Input Attachments [#prompt-input-attachments]

**PromptInputAttachments** adds image/file attachment to the composer via file picker, paste, and drag-and-drop. It renders preview thumbnails with per-item removal, a hovercard preview, media-category auto-detection (image / video / audio / document), and upload-state chips. Attachments match the standard base64 image content model, using the copy-owned `readFileAsDataUrl` helper from `lib/browser-utils`.

Preview [#preview]

```tsx
'use client';

/**
 * @file prompt-input-attachments-demo.tsx
 * @description Docs preview for `PromptInputAttachments`. Attach images via the
 * picker, paste, or drag-and-drop; remove a thumbnail; see the payload update.
 */
import * as React from 'react';
import {
  PromptInput,
  PromptInputProvider,
  PromptInputSubmit,
  PromptInputTextarea,
  PromptInputTools,
  type PromptAttachment,
} from '@/components/prompt-input';
import { PromptInputAttachments } from '@/components/prompt-input-attachments';

export default function PromptInputAttachmentsDemo() {
  const [sent, setSent] = React.useState<{ text: string; count: number } | null>(
    null,
  );

  return (
    <PromptInputProvider>
      <div className="flex w-full max-w-xl flex-col gap-2">
        <PromptInputAttachments />
        <PromptInput
          onSubmit={(text: string, attachments: PromptAttachment[]) =>
            setSent({ text, count: attachments.length })
          }
        >
          <PromptInputTextarea placeholder="Attach an image (drop / paste / picker), then send…" />
          <PromptInputTools>
            <span className="hidden min-w-0 truncate px-2 text-xs text-muted-foreground sm:inline">
              Images travel as base64 ContentParts
            </span>
            <PromptInputSubmit className="shrink-0" />
          </PromptInputTools>
        </PromptInput>
        {sent && (
          <p className="text-sm text-muted-foreground">
            Sent “{sent.text || '(no text)'}” with {sent.count} attachment(s).
          </p>
        )}
      </div>
    </PromptInputProvider>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/conversation/prompt-input-attachments
```

Data source & dependencies [#data-source--dependencies]

**Data source:** emits attachment changes and reads local files — works with any backend. Recommended producer: `useChat` from `@localmode/react` (on-device, optional).

* `@localmode/ui/lib/browser-utils` — `readFileAsDataUrl` for file reading (installed automatically as a registry dependency)
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `prompt-input-attachments.tsx` — `PromptInputAttachments`
* `lib/browser-utils.ts` — generic browser helpers (`readFileAsDataUrl`)
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**PromptInputAttachments**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `array` | — | Current attachments (controlled). Falls back to the provider when omitted. |
| `onChange` | `function` | — | Reports the new attachment list after add/remove (controlled). |
| `accept` | `string` | `"image/*"` | Accept filter for the picker / drop. |
| `multiple` | `boolean` | `true` | Allow multiple files. |

Examples [#examples]

Inside a `PromptInputProvider` [#inside-a-promptinputprovider]

```tsx
import {
  PromptInput,
  PromptInputProvider,
  PromptInputSubmit,
  PromptInputTextarea,
  PromptInputTools,
} from '@/components/prompt-input';
import { PromptInputAttachments } from '@/components/prompt-input-attachments';

<PromptInputProvider>
  <PromptInputAttachments />
  <PromptInput onSubmit={(text, attachments) => send(text, { images: attachments })}>
    <PromptInputTextarea />
    <PromptInputTools><span /><PromptInputSubmit /></PromptInputTools>
  </PromptInput>
</PromptInputProvider>
```

Customization [#customization]

Change the `accept` filter, the thumbnail size, or the `mediaCategory` mapping. The provider integration means attachments clear automatically on submit; drop the provider and use the controlled `value`/`onChange` props for fully external control.

These primitives are presentational and hook-driven: they render props and emit callbacks, holding only local view state. The orchestration state (e.g. `useChat`) lives in your app. Every surface uses shadcn/ui CSS-variable utilities, so it inherits your theme — restyle the copied file freely.