File Preview

Pro

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# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># 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.

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Anatomy#

import {  FilePreview,  FilePreviewHeader,  FilePreviewIcon,  FilePreviewInfo,  FilePreviewTitle,  FilePreviewMeta,  FilePreviewActions,  FilePreviewDownload,  FilePreviewClose,  FilePreviewThumbnails,  FilePreviewViewport,  FilePreviewToolbar,  FilePreviewThumbnailsToggle,  FilePreviewPageNav,  FilePreviewZoomOut,  FilePreviewZoomMenu,  FilePreviewZoomIn,  FilePreviewFit,  FilePreviewRotate,  FilePreviewFullscreen,} from "@/components/ui/file-preview"<FilePreview>  <FilePreviewHeader>    <FilePreviewIcon />    <FilePreviewInfo>      <FilePreviewTitle />      <FilePreviewMeta />    </FilePreviewInfo>    <FilePreviewActions>      <FilePreviewDownload />      <FilePreviewClose />    </FilePreviewActions>  </FilePreviewHeader>  <FilePreviewThumbnails />  <FilePreviewViewport>    <FilePreviewToolbar>      <FilePreviewThumbnailsToggle />      <FilePreviewPageNav />      <FilePreviewZoomOut />      <FilePreviewZoomMenu />      <FilePreviewZoomIn />      <FilePreviewFit />      <FilePreviewRotate />      <FilePreviewFullscreen />    </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.

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

API reference#

FilePreview

The root: resolves the file (a Blob becomes an object URL), picks the renderer, owns the view state and handles the keyboard shortcuts. Forwards native <div> props except onError; every part that renders an element accepts className, merged last.

PropTypeDefaultDescription
fileBlob | null-The file to show: a File from an input or a drop, or any Blob.
srcstring-Or a URL to read it from. Ignored when file is set.
namestring-Display name. Defaults to the File's name, else the last segment of src.
typestring-MIME type. Defaults to the File's type; otherwise the name's extension decides.
sizenumber-Size in bytes, for the meta line. Defaults to the File's size.
kind"pdf" | "image" | "video" | "audio" | "text" | "unsupported"-Pick the renderer yourself instead of detecting it from the type and the name.
zoomFilePreviewZoom-Controlled zoom: "fit", "width", or a scale (1 is 100%).
defaultZoomFilePreviewZoom"fit"Initial zoom when uncontrolled.
onZoomChange(zoom: FilePreviewZoom) => void-Called when the zoom changes, from a control, a key or a pinch.
rotationnumber-Controlled rotation in degrees, in multiples of 90.
defaultRotationnumber0Initial rotation when uncontrolled.
onRotationChange(rotation: number) => void-Called when the view rotates.
defaultPagenumber1The page a PDF opens at.
onPageChange(page: number) => void-Called as the reader moves through the pages.
minZoomnumber0.1Lowest scale the zoom controls reach (a fit may go lower).
maxZoomnumber5Highest scale the zoom controls reach.
defaultThumbnailsOpenbooleantrueWhether the page rail starts open (it only exists from md up).
textLayerbooleantrueLay PDF.js's selectable text over each page, so text can be selected, copied and found.
pdfOptionsFilePreviewPdfOptions-Passed to PDF.js's getDocument: cMapUrl, standardFontDataUrl, wasmUrl, password, withCredentials.
onDownload() => void-Replace the built-in download (an anchor with the file's name; a cross-origin URL is fetched first).
variant"default" | "outline" | "plain""default"The frame: the edge ring with the smallest lift, the bare --border ring, or no frame at all.
Data attributeDescription
data-kind"pdf", "image", "video", "audio", "text" or "unsupported".
data-status"loading", "ready" or "error".
data-fullscreenPresent while the preview is full screen.

FilePreviewHeader

The identity bar across the top, ruled off from the canvas. Forwards all native <div> props.

FilePreviewIcon

The file's FileIcon, its type and extension taken from the file in hand. Forwards FileIcon's props (except type and extension) and native <svg> props.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "xl""sm"Height: 32, 40, 48 or 64px.
variant"default" | "soft" | "solid""default"FileIcon's fill treatment.
labelstring-Override the stamped text outright.

FilePreviewInfo

The name-over-meta column; it lets a long name truncate. Forwards all native <div> props.

FilePreviewTitle

The file's name on one truncated line, the whole name on hover. Children replace it. Forwards all native <div> props.

FilePreviewMeta

A line composed from what is known (format, size, then the page count, pixel size, running time or line count as the file loads). Children replace it. Forwards all native <div> props.

FilePreviewActions

The trailing actions on the header's right edge. Forwards all native <div> props.

FilePreviewDownload

Saves the file under its own name; icon-only unless you pass children. A Button: takes every Button prop except iconOnly, and preventDefault() in onClick skips the download.

PropTypeDefaultDescription
variantButtonProps["variant"]"ghost"Any Button variant.
sizeButtonProps["size"]"sm"Any Button size.
staticbooleantrueKept for compatibility; controls no longer scale on press.

FilePreviewClose

An icon-only close button. Inside FilePreviewDialog it closes the dialog; anywhere else wire its onClick. Takes every Button prop except iconOnly.

PropTypeDefaultDescription
variantButtonProps["variant"]"ghost"Any Button variant.
sizeButtonProps["size"]"sm"Any Button size.
staticbooleantrueKept for compatibility; controls no longer scale on press.

FilePreviewThumbnails

The page rail beside the canvas, from md up, for a document with more than one page; renders nothing otherwise. Thumbnails paint as the rail scrolls. Forwards all native <nav> props.

Data attributeDescription
data-state"open" or "closed" (hidden), toggled by FilePreviewThumbnailsToggle.
data-activeOn the current page's thumbnail, which wears the ink ring.
data-page-numberOn each thumbnail, its 1-based page number.

FilePreviewViewport

The canvas: renders the file in its own focusable scroller and holds what floats over it, the toolbar first. It owns the gestures (⌘/Ctrl + wheel, pinch, drag to pan a zoomed photo, double-click). Forwards all native <div> props.

PropTypeDefaultDescription
contentPropsOmit<React.ComponentProps<"div">, "ref" | "children">-Props for the scroller inside, the element that takes focus and scrolls.
Data attributeDescription
data-pannableOn the scroller while a photo is larger than the canvas and drags to pan.
CSS variableDescription
--surfaceSet to var(--muted), read by controls inside the canvas.
--file-preview-toolbar-spaceRoom reserved at the bottom of the scroll area while a toolbar floats over it.

FilePreviewToolbar

The controls' bar, a Toolbar that takes every Toolbar prop. Floats bottom centre inside the viewport, docks in the header with variant="plain", and renders nothing for a file it cannot control or one that failed to load.

PropTypeDefaultDescription
variant"floating" | "outline" | "plain" | "solid""floating"The Toolbar's surface.
size"sm" | "md" | "lg""md"The Toolbar's control size.
scrollablebooleantrueScroll sideways instead of overflowing a narrow canvas.

FilePreviewZoomOut

One zoom step out (shortcut -). Each control is a ToolbarButton held still on press and takes its props except tooltip and shortcut; it renders nothing when the file cannot zoom.

FilePreviewZoomIn

One zoom step in (shortcut +), disabled at maxZoom. Takes ToolbarButton props except tooltip and shortcut.

FilePreviewZoomMenu

The live zoom readout, which opens fit to page, fit to width and the fixed levels in a dropdown. Takes ToolbarButton props except tooltip, shortcut and children.

PropTypeDefaultDescription
presetsnumber[]ZOOM_PRESETSThe fixed levels listed under the fit modes (1 is 100%): [0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4], filtered to minZoom/maxZoom.

FilePreviewFit

Fit to page (shortcut 0) and fit to width as a pair of toggles; neither is pressed after a manual zoom.

PropTypeDefaultDescription
disabledbooleanfalseDisable both toggles.

FilePreviewRotate

A quarter turn of the view (shortcut R, or ⇧R counterclockwise); the download still saves the original. Takes ToolbarButton props except tooltip and shortcut.

PropTypeDefaultDescription
direction"clockwise" | "counterclockwise""clockwise"Which way the view turns.

FilePreviewPageNav

Previous page, a page field that jumps on Enter (Escape restores it), the count, next page. Renders only for a paged document; takes only className.

FilePreviewThumbnailsToggle

Shows or hides the page rail; renders only where the rail can exist. Takes ToolbarButton props except tooltip and shortcut.

FilePreviewFullscreen

The whole preview full screen (shortcut F); renders nothing where the browser cannot go full screen. Takes ToolbarButton props except tooltip and shortcut.

FilePreviewDialog

The preview in a modal on Radix Dialog with the Dialog's scrim: edge to edge on a phone, focus on the canvas, and it keeps the last file while fading out. Takes every FilePreview prop (children are the preview's parts); the inner preview defaults to variant="plain".

PropTypeDefaultDescription
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void-Called when the dialog opens or closes (Esc, the scrim, FilePreviewClose).
titlestringthe file's nameThe dialog's visually hidden accessible title.
classNamestring-Classes for the dialog surface.
previewClassNamestring-Classes for the FilePreview inside.
Data attributeDescription
data-state"open" or "closed", on the dialog surface and the overlay.
CSS variableDescription
--surfaceSet to var(--popover) on the dialog surface.

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.