File Preview

Look at a file where it is instead of opening another tab. PDFs page through with selectable text, images zoom and pan, and one set of controls fits, zooms, rotates and pages every kind.

Koala Q3 2026 report.pdf
PDF · 273.0 KB

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add file-preview

Usage#

Hand the root a file (a File from an input or a drop) or a src. It works out how to show it from the type and the name, and every part below reads the file, the zoom and the page from it. PDFs render with Mozilla's PDF.js, which is loaded only when a PDF is opened.

preview.tsxTSX
import {  FilePreview,  FilePreviewHeader,  FilePreviewIcon,  FilePreviewInfo,  FilePreviewTitle,  FilePreviewMeta,  FilePreviewActions,  FilePreviewDownload,  FilePreviewViewport,  FilePreviewToolbar,  FilePreviewZoomOut,  FilePreviewZoomMenu,  FilePreviewZoomIn,  FilePreviewFit,} from "@/components/ui/file-preview"<FilePreview file={file}>  <FilePreviewHeader>    <FilePreviewIcon />    <FilePreviewInfo>      <FilePreviewTitle />      <FilePreviewMeta />    </FilePreviewInfo>    <FilePreviewActions>      <FilePreviewDownload />    </FilePreviewActions>  </FilePreviewHeader>  <FilePreviewViewport>    <FilePreviewToolbar>      <FilePreviewZoomOut />      <FilePreviewZoomMenu />      <FilePreviewZoomIn />      <FilePreviewFit />    </FilePreviewToolbar>  </FilePreviewViewport></FilePreview>

Preview an upload#

The flow it exists for: files arrive in a File Upload list and a click opens them over the page with FilePreviewDialog, instead of in a new tab. Drop your own files in, they preview straight from the disk and never leave the browser. The dialog is edge to edge on a phone, lands focus on the canvas so the keys work at once, and keeps showing the file while it fades out, so clearing it on close is safe.

Drag and drop your files, or
PDF, images, text, video or audio, up to 50 MB

Every kind of file#

The renderer follows the file. A PDF pages through with selectable text and a page rail. An image zooms and, once it no longer fits, drags to pan (double-click toggles 100%). Text and code read as a numbered sheet. Video plays in the Video Player and audio in a compact player whose seek bar is the clip's own waveform. Anything else says so and offers its download. A control with nothing to do for the file renders nothing, and the bar folds the separator it leaves behind.

living-room.jpg
JPG · 751.1 KB
living-room.jpg

Fit and zoom#

"fit" shows the whole page and "width" fills the canvas side to side; both follow the canvas as it resizes. A number is a fixed level, 1 being the file's actual size. The buttons walk the familiar steps, the readout opens every level, and ⌘ + wheel or a pinch zooms continuously. Every change holds a point still, the pointer for a wheel or a pinch and the centre otherwise, so a zoom never throws you somewhere else in the file. A photo never grows past its own pixels on "fit": an upscaled photo is a blurred one.

living-room.jpg
JPG · 751.1 KB
living-room.jpg

Docked toolbar#

The toolbar floats over the canvas when it sits in FilePreviewViewport. Put it in the header instead, with variant="plain", for a bar docked above the document like a desktop reader. The same controls, the same behaviour. This one opens at defaultZoom="width", the reading layout.

Koala Q3 2026 report.pdf
PDF · 273.0 KB

Variants#

The frame follows the library's contours. Default is the edge ring with the smallest lift, outline the ring alone, and plain drops the frame for a preview that sits in a surface which already has one: a pane, a card, the dialog (which uses it). None of them paints a fill; the canvas is the one ground, so the document reads the same on every surface.

living-room.jpg
JPG · 751.1 KB
living-room.jpg

default

living-room.jpg
JPG · 751.1 KB
living-room.jpg

outline

living-room.jpg
JPG · 751.1 KB
living-room.jpg

plain

Only the parts you need#

Every part can go. Here the canvas stands alone, with no header and no bar: an image inline in a card or a chat message. The keys, the wheel and the pinch still zoom it, because they live on the canvas, not on the buttons. Leave out the rail, the page field or any single control the same way.

living-room.jpg

Controlled#

Hold the zoom yourself with zoom and onZoomChange: here a Slider outside the preview drives it, and the buttons, the menu and a pinch inside it report back. rotation works the same way, and defaultPage with onPageChange opens a PDF at a page and follows the reader through it.

50%
Koala Q3 2026 report.pdf
PDF · 273.0 KB

Keyboard and gestures#

The keys work while focus is anywhere in the preview, except in the page field and inside a video player, which keeps its own. The dialog puts focus on the canvas as it opens, and the arrow keys, Page Up/Down and Space scroll it natively.

+−Zoom in and out, one step (⌘/Ctrl too, so the browser's own zoom stays out of it)
0Fit the page
⌘/CtrlWheelZoom around the pointer (a trackpad pinch arrives the same way)
R⇧RRotate right, rotate left
←→Previous and next page, when nothing scrolls sideways
FFull screen
EscLeave full screen, then close the dialog

On a touch screen one finger scrolls natively, with its momentum, and two pinch. A photo larger than the canvas drags to pan; a document keeps its drag for selecting text.

PDF setup#

PDF.js runs its parser in a Web Worker. In a Next.js app (Turbopack or webpack) the component starts it from the package on first use, with nothing to configure. With another bundler, or to serve the worker yourself (a CDN, a shared worker), set GlobalWorkerOptions.workerSrc before the first PDF opens and the component leaves it alone. Documents that rely on the standard fonts, on CJK character maps or on JPEG 2000 images render best with PDF.js's assets available: copy them into public/ and pass their folders through pdfOptions.

report-preview.tsxTSX
// cp -r node_modules/pdfjs-dist/{cmaps,standard_fonts,wasm} public/pdfjs/<FilePreview  src="/files/q3-report.pdf"  pdfOptions={{    cMapUrl: "/pdfjs/cmaps/",    standardFontDataUrl: "/pdfjs/standard_fonts/",    wasmUrl: "/pdfjs/wasm/",  }}/>
pdf-worker.tsTSX
// Vite: the worker's URL comes from the bundler, then PDF.js is told where it is.import { GlobalWorkerOptions } from "pdfjs-dist"import workerSrc from "pdfjs-dist/build/pdf.worker.min.mjs?url"GlobalWorkerOptions.workerSrc = workerSrc

API reference#

FilePreview

The root. Takes the file as file (a File or Blob) or src, with name, type and size to name it when a URL can't, and kind to pick the renderer yourself (pdf · image · text · video · audio · unsupported). The view: zoom / defaultZoom ("fit", "width" or a scale; default "fit") with onZoomChange, rotation / defaultRotation with onRotationChange, defaultPage and onPageChange, minZoom (0.1) and maxZoom (5). Also variant (default · outline · plain), defaultThumbnailsOpen, textLayer (PDF selectable text, on), pdfOptions (passed to PDF.js) and onDownload to replace the built-in download.

FilePreviewHeader · FilePreviewIcon · FilePreviewInfo · FilePreviewTitle · FilePreviewMeta · FilePreviewActions

The identity bar. FilePreviewIcon is the file's FileIcon, FilePreviewTitle its name and FilePreviewMeta a line composed from what is known (format, size, then the page count, the pixel size, the running time or the line count as the file loads). Children replace the title and the meta.

FilePreviewDownload · FilePreviewClose

Ghost Buttons for the header. Download saves the file under its own name (a cross-origin URL is fetched first, since the browser ignores download there); pass children for a labelled button. Close closes a FilePreviewDialog; anywhere else, wire its onClick.

FilePreviewThumbnails

The page rail beside the canvas, from md up, for a document with more than one page. Thumbnails paint as the rail scrolls; the current page wears the brand ring.

FilePreviewViewport

The canvas: renders the file and holds what floats over it. It owns the gestures (wheel, pinch, drag to pan, double-click) and the scroller that takes focus; contentProps reaches that scroller.

FilePreviewToolbar

The bar the controls live in, a Toolbar that takes the Toolbar's props (variant, size, scrollable, on by default). Floats in the viewport, docks in the header.

FilePreviewZoomOut · FilePreviewZoomMenu · FilePreviewZoomIn · FilePreviewFit · FilePreviewRotate · FilePreviewPageNav · FilePreviewThumbnailsToggle · FilePreviewFullscreen

The controls, each a ToolbarButton with its tooltip and shortcut. FilePreviewZoomMenu takes presets, FilePreviewRotate a direction. Each renders nothing when it has nothing to do for the file in hand, and the fullscreen control nothing where the browser can't go full screen.

FilePreviewDialog

The preview in a modal: every FilePreview prop, plus open, defaultOpen, onOpenChange, title (the accessible title, the file name by default) and previewClassName. Built on Radix Dialog with the Dialog's own scrim.

FAQ#

PDFs (with PDF.js), every image format the browser decodes (JPEG, PNG, WebP, AVIF, GIF, SVG), text and source files (Markdown, JSON, CSV, code), and the video and audio formats the browser plays. Word, Excel or PowerPoint files are not rendered in the browser: convert them to PDF on your server and preview that, or let the canvas offer the download.