- Overview
- Anatomy
- Header
- Navigation
- Renderers
The File Viewer is a single entry point for previewing any file. It detects the type from the file name (then the MIME type) and lazy-loads only the matching viewer, so the heavy parser for a format you never open is never downloaded.
It is the flagship document component. The format renderers are not peers of File Viewer; they are routes inside it: PDF, DOCX, Image, PPTX, XLSX, and CSV. File Viewer also owns inline, client-side rendering for text, code, JSON, Markdown, and HTML.
"use client";
import * as React from "react";
import { useMediaQuery } from "@/hooks/use-media-query";
import { cn } from "@/lib/utils";
import {
FileViewer,
FileViewerContent,
FileViewerDocument,Installation
pnpm dlx shadcn@latest add @retab/file-viewer
This pulls in the format-specific viewers it delegates to, plus the
Markdown Viewer
for Markdown documents, custom fixed-line virtualization for the Code Viewer,
and @chenglou/pretext for the wrapped Text and Markdown surfaces.
Usage
import { FileViewerPreview } from "@/components/ui/file-viewer";
export function Example() {
return (
<FileViewerPreview
source={{ kind: "url", url: "/report.pdf", fileName: "report.pdf" }}
/>
);
}source.fileName drives type detection and the download button. If you only
have a URL, the URL is used for display and its basename is used for downloads;
pass source.mimeType when the extension is missing or ambiguous, or
category to force a category.
Composition Boundaries
FileViewerProvider is the file router and file-scoped provider stack.
FileViewer is the frame beneath it. Do not use either one to own workflow
semantics. Do use the named anatomy when a file-backed surface needs sidebars,
composed panes, or file-scoped navigation.
Use the file viewer parts when a workflow needs one file header above domain chrome:
<FileViewerProvider source={source} defaultSidebarOpen>
<FileViewer>
<FileViewerHeader>
<FileViewerSidebarTrigger />
<FileViewerTitle />
<FileViewerControls />
</FileViewerHeader>
<FileViewerContent>
<FileViewerSidebar aria-label="Document sections" />
<FileViewerInset>
<FileViewerViewport>
<FileViewerDocument />
</FileViewerViewport>
</FileViewerInset>
</FileViewerContent>
</FileViewer>
</FileViewerProvider>FileViewerTitle answers what file this is, and FileViewerControls renders
operational controls registered by the active file renderer.
FileViewerDocument is the routed file renderer.
See Anatomy for the compound API and Sidebar for file-scoped navigation.
Use the preview shell for the common case:
<FileViewerPreview source={source} />Use the composed shell when the file surface needs file-scoped sidebars or custom header composition:
<FileViewerProvider source={source}>
<FileViewer>
<FileViewerHeader>
<FileViewerTitle />
<FileViewerControls />
</FileViewerHeader>
<FileViewerContent>
<FileViewerInset>
<FileViewerViewport>
<FileViewerDocument />
</FileViewerViewport>
</FileViewerInset>
</FileViewerContent>
</FileViewer>
</FileViewerProvider>Use FileViewerPreview for standalone nested leaf previews where the parent
already owns the surrounding surface:
<FileViewerPreview source={attachment.source} />Supported types
| Category | Extensions | Rendered by |
|---|---|---|
pdf | PDF Viewer | |
| Word | docx | DOCX Viewer |
| Spreadsheet | xlsx, xls, xlsm | XLSX Viewer |
| Presentation | pptx | PPTX Viewer |
| Tabular | csv, tsv | CSV Viewer |
| Image | png, jpg, jpeg, gif, webp, avif, svg, bmp, ico, tif, tiff | Image Viewer |
| Markdown | md, markdown, text/markdown | Markdown Viewer |
| HTML | html, htm | sandboxed iframe |
| Text | txt, text | Pretext Text Viewer |
| Code & logs | log, json, xml, yaml, mdx, ts, py, go, … | Code Viewer |
Anything unrecognized falls back to a download card.
The Email Viewer also belongs to
the File Viewer category, but it is a compound MIME viewer rather than a routed
FileViewerDocument renderer: its body and attachment leaves render through
File Viewer primitives.
How it performs
- Code-split delegation. Each format-specific viewer is a
React.lazyimport — opening a PDF never loads SheetJS, pptxviewjs, or the others. - Code and text split. Code-like files render as fixed lines through the Code Viewer with line numbers. Prose text renders through the Pretext Text Viewer with natural wrapping and custom variable-height virtualization.
- Markdown has a dedicated continuous document path. Markdown URL, Blob,
inline text, and MIME-only sources route to
MarkdownViewer, which keeps Pretext-informed virtual chunks as an internal layout strategy while React/GFM owns the visible document rendering. The Text Viewer remains for prose text that is not Markdown. - Safe HTML. Standalone
.htmlfiles render inside a<iframe sandbox>with no script execution and no same-origin access, so untrusted documents preview without running code or reaching the app.
API Reference
| Prop | Owner | Description |
|---|---|---|
source | FileViewerProvider | Canonical file source. URL, Blob, and text sources carry file metadata with the payload identity. |
category | FileViewerProvider | Force a category, bypassing detection. |
defaultSidebarOpen | FileViewerProvider | Initial sidebar open state for composed viewers. |
sidebarMode | FileViewer | "auto", "inline", or "overlay" sidebar layout mode. |
side | FileViewerSidebar | Which side the sidebar mounts on. |
className | FileViewer | Optional class on the viewer root element. |
The detectCategory(fileName, mimeType?) helper is also exported if you need to
branch on the resolved category yourself.
Source
"use client";
export type { ViewerSource } from "./file-viewer-core";
export {
FileViewerContent,
FileViewerInset,
FileViewerLegend,
FileViewerViewport,
type FileViewerContentProps,
type FileViewerInsetProps,
type FileViewerLegendProps,
type FileViewerViewportProps,