File Preview
One file rendered as whatever it actually is — image, text, audio, or an honest “no preview” panel. The MIME type decides, never the extension: a .png served as text/html is the oldest upload attack there is.
src/components/ui/file-preview.tsxAbout
From the component's own source, where it was written.
One file, rendered as whatever it actually is.
The dispatcher a file store needs: an image previews, text and code render highlighted, audio gets a player, and everything else gets an honest "no preview" panel with a download rather than a broken <img>.
**The MIME type decides, not the extension.** A .png served as text/html is the oldest upload attack there is, and a previewer that trusts the filename renders it as markup. Extension is only consulted when the store reports no type at all.
Text is capped. A 40MB log rendered into the DOM freezes the tab, and the point of a preview is to decide whether you want the file — the first few hundred lines answer that.
Install
npx astralyx-ui add file-preview
Usage
import { FilePreview } from '@/components/ui/file-preview'
<FilePreview file={file} onDownload={download} />Composer
Change the props and the source below follows.
hero-dusk.jpg
image/jpeg · 822 KB
<FilePreview
file={{ name: '…', type: 'image/jpeg', … }}
onDownload={download}
/>Examples
A text file
<FilePreview file={{ name: 'customers.csv', type: 'text/csv', text }} />Something with no preview
<FilePreview file={{ name: 'archive.tar.zst', type: 'application/zstd' }} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
FilePreview
Extends Omit<ComponentProps<'div'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| file | PreviewFile | required | { name, type?, size?, url?, text?, language? }. text is already fetched — this renders, it does not load. |
| onDownload | (file: PreviewFile) => void | — | — |
| maxChars | number | 20_000 | A 40MB log rendered into the DOM freezes the tab, and a preview only has to answer whether you want the file. |
| downloadLabel | string | 'Download' | — |
| noPreviewLabel | string | 'No preview for this file type.' | — |
| truncatedLabel | (shown: number, total: number) => ReactNode | (shown, total) => `Showing the first ${shown.toLocaleString()} of ${total.toLocaleString()} characters.` | — |
| actions | ReactNode | — | — |
Functions
| Function | Signature | Description |
|---|---|---|
| previewKind | (file: PreviewFile) => 'image' | 'audio' | 'video' | 'text' | 'binary' | MIME first; the extension is a fallback, never an override. |
Types
PreviewFile
One file, rendered as whatever it actually is.
| Field | Type | Description |
|---|---|---|
| name | string | — |
| type? | string | The store’s MIME type decides. The extension is consulted only when there is no type at all, never as an override. |
| size? | number | Bytes. |
| url? | string | For images, audio and video. |
| text? | string | Already-fetched contents, for text and code. |
| language? | string | Highlighting hint for text. |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| audio / video | real elements | Audio uses this kit’s AudioPlayer; video uses a native <video>, which brings buffering, captions and OS controls with it. |