Attachment Preview

A file chip with an icon chosen from the MIME type. Never from the extension: a `.dat` that is really a PDF should show a PDF, and a `.pdf` that is really an executable should not.

src/components/ui/attachment-preview.tsx

About

From the component's own source, where it was written.

An attached file: thumbnail, size, progress.

The icon is chosen from the MIME type, not the file extension. Extensions are user-supplied and routinely wrong or absent, and invoice.pdf.exe is the oldest trick there is — deriving the icon from what the file claims to be by name would draw a document icon on an executable.

An upload in progress is the same component as a finished one, so the layout does not jump when it completes. Files change height on completion in most implementations and the list reflows under the pointer.

Install

npx astralyx-ui add attachment-preview

Usage

import { AttachmentPreview } from '@/components/ui/attachment-preview'

<AttachmentPreview attachment={file} onRemove={remove} onDownload={download} />

Composer

Change the props and the source below follows.

contract-renewal-2026.pdf

822 KB

dashboard-mock.png

2.3 MB

export.csv

19 KB
Props
tsx
<AttachmentPreview attachment={file} onRemove={remove} onDownload={download} />

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

AttachmentPreview

An attached file: thumbnail, size, progress.

Extends Omit<ComponentProps<'div'>, 'children'>.

PropTypeDefaultDescription
attachmentAttachmentrequired{ id, name, type?, size?, url?, thumbnail?, progress?, error? }.
onRemove(id: string) => void——
onDownload(attachment: Attachment) => void——
compactbooleanfalseA single-line chip for an inline message, rather than a card in a list.

Functions

FunctionSignatureDescription
attachmentIconFor(type: string | undefined) => ForwardRefExoticComponent<Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>>—

Types

Attachment

FieldTypeDescription
idstring—
namestringThe extension is preserved when the name is trimmed, since it is the most informative part of a long filename.
type?stringMIME type. Drives the icon — not the extension.
size?numberBytes.
url?string—
thumbnail?string—
progress?number0–1 while uploading. Omit once complete; a bar stuck at 100% is indistinguishable from one that stalled.
error?ReactNode—

Behaviour

TopicDetailDescription
iconfrom MIME typeChosen from type, falling back to the extension only when no type is given.