Attachment Chip

A file attachment chip — a status-aware label with an optional image thumbnail and a hover preview (image, PDF, or file summary). Shared by the chat composer and sent-message rows.

Q3-drift-report.pdf

whitfield-fee-schedule.xlsx

portfolio-chart.png

portfolio-chart.png

<AttachmentChip  filename="portfolio-chart.png"  mediaType="image/png"  hasPreview  previewStatus="ready"  previewUrl={objectUrl}/>

Usage

import { AttachmentChip } from "@reva/ui";

AttachmentChip renders a single file attachment. It has two surfaces, chosen by whether you pass a status:

  • Message chip (no status) — a plain bordered chip for a file already attached to a sent message.
  • Composer chip (status set) — a status-aware chip for a file being uploaded: uploading shows a spinner, error renders a danger-toned chip, and onRemove adds an X button.

Image files render a ~48px inline thumbnail once previewUrl resolves, and any chip with hasPreview reveals a larger preview on hover.

PromptInputAttachment is a thin wrapper over this primitive, so composer chips and message chips share one surface.

Presentation only — you own the fetch

AttachmentChip does no fetching. It is pure presentation: you load the bytes (subject to your own auth / API) and feed the chip a resolved object URL plus the fetch status.

  • previewUrl — the object URL for the loaded bytes. Drives the image thumbnail and the hover-card body.
  • previewStatusidle / loading / ready / error, drives the hover-card spinner and error fallback.
  • hasPreview — when true, the chip wraps itself in a hover card and emits onPreviewOpenChange(open) so you can lazy-fetch the bytes only when the preview opens. Load images eagerly (pass previewUrl up front) so the thumbnail paints without a hover.
function ChatAttachment({ documentId, filename, mediaType }) {
  const [open, setOpen] = useState(false);
  const preview = useDocumentPreview(documentId, open || mediaType?.startsWith("image/"));

  return (
    <AttachmentChip
      filename={filename}
      mediaType={mediaType}
      hasPreview={!!documentId}
      previewUrl={preview.objectUrl}
      previewStatus={preview.status}
      onPreviewOpenChange={setOpen}
    />
  );
}

Examples

Message chips

A plain bordered chip per attached file. Image files paint an inline thumbnail when previewUrl is supplied.

Q3-drift-report.pdf

whitfield-fee-schedule.xlsx

portfolio-chart.png

portfolio-chart.png

<AttachmentChip filename="Q3-drift-report.pdf" mediaType="application/pdf" /><AttachmentChip filename="whitfield-fee-schedule.xlsx" mediaType="application/vnd.ms-excel" /><AttachmentChipfilename="portfolio-chart.png"mediaType="image/png"hasPreviewpreviewStatus="ready"previewUrl={objectUrl}/>

Upload statuses

Composer chips across the upload lifecycle — uploading, ready, and error. Pass onRemove to add the X button.

Uploading cashflow-2025.csvcashflow-2025.csv
isa-transfer-form.pdf
kyc-pack.zipUpload failed
<AttachmentChip filename="cashflow-2025.csv" status="uploading" /><AttachmentChip filename="isa-transfer-form.pdf" status="ready" onRemove={remove} /><AttachmentChip filename="kyc-pack.zip" status="error" onRemove={remove} />

Hover preview

With hasPreview, hover reveals a larger preview: images render inline, PDFs in an iframe, and everything else as a tidy file summary.

portfolio-chart.png

portfolio-chart.png

cashflow-2025.csv

<AttachmentChip  filename="portfolio-chart.png"  mediaType="image/png"  hasPreview  previewStatus="ready"  previewUrl={objectUrl}/>

Preview fetch states

The hover card reflects previewStatus: a spinner while loading, the bytes once ready, and a fallback when the fetch fails.

portfolio-chart.png

Hover the chip — the preview card reflects the selected fetch state.

Accessibility

  • The remove button is named "Remove {filename}".
  • uploading chips give the spinner an accessible "Uploading {filename}" label; error chips carry a visually hidden "Upload failed".
  • The chip's hover trigger is a wrapper div (not a button) so the composer chip can nest a real remove button without invalid nested-button HTML.

Props

PropTypeDefaultDescription
filenamestringThe file label; also names the remove button. Required.
mediaTypestringMIME type — selects the type glyph and the hover-preview renderer (image / PDF / summary).
status"uploading" | "ready" | "error"Set it for a composer chip (status glyph + optional remove). Omit it for the plain message chip.
onRemove() => voidRenders the remove (X) button when provided.
previewUrlstringResolved object URL for the loaded bytes — drives the image thumbnail and the hover-card body. The component never fetches; you supply this.
previewStatus"idle" | "loading" | "ready" | "error"Preview-fetch lifecycle — drives the hover-card spinner / error fallback.
hasPreviewbooleanfalseWrap the chip in a hover card and emit onPreviewOpenChange. Omit for a bare chip with no preview.
onPreviewOpenChange(open: boolean) => voidFired with the hover-open state so you can gate (lazy-load) the byte fetch.

On this page