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.tsx

About

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

hero-dusk.jpg
Props
tsx
<FilePreview
  file={{ name: '…', type: 'image/jpeg', … }}
  onDownload={download}
/>

Examples

A text file

Something with no preview

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'>.

PropTypeDefaultDescription
filePreviewFilerequired{ name, type?, size?, url?, text?, language? }. text is already fetched — this renders, it does not load.
onDownload(file: PreviewFile) => void——
maxCharsnumber20_000A 40MB log rendered into the DOM freezes the tab, and a preview only has to answer whether you want the file.
downloadLabelstring'Download'—
noPreviewLabelstring'No preview for this file type.'—
truncatedLabel(shown: number, total: number) => ReactNode(shown, total) => `Showing the first ${shown.toLocaleString()} of ${total.toLocaleString()} characters.`—
actionsReactNode——

Functions

FunctionSignatureDescription
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.

FieldTypeDescription
namestring—
type?stringThe store’s MIME type decides. The extension is consulted only when there is no type at all, never as an override.
size?numberBytes.
url?stringFor images, audio and video.
text?stringAlready-fetched contents, for text and code.
language?stringHighlighting hint for text.

Behaviour

TopicDetailDescription
audio / videoreal elementsAudio uses this kit’s AudioPlayer; video uses a native <video>, which brings buffering, captions and OS controls with it.