# How pi-web Handles File Previews for Images, Audio, PDF, and DOCX

> Discover how pi-web streamlines file previews for images, audio, PDF, and DOCX. Learn about its viewer components, size limits, and dedicated API streaming.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-07-26

---

**pi-web delegates file preview rendering to type-specific viewer components within a central `FileViewer` orchestrator, enforcing 10 MiB size limits for images and DOCX files while streaming content through dedicated API endpoints.**

The `agegr/pi-web` repository provides a React-based file manager where the **pi-web file preview** system distinguishes between binary assets and documents to optimize rendering performance. The implementation centers on the `FileViewer` component in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx), which coordinates with utility functions in [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts) to classify extensions and select the appropriate rendering strategy.

## File Type Classification

Before rendering, the system categorizes files using three helper predicates exported from [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts):

- **`isImagePath(filePath)`** – Validates extensions against the `IMAGE_EXT_TO_MIME` mapping.
- **`isAudioPath(filePath)`** – Checks extensions against `AUDIO_EXT_TO_MIME`.
- **`isDocumentPreviewPath(filePath)`** – Determines if the file is a PDF or DOCX via `documentPreviewKind`.

The `FileViewer` component applies these checks sequentially to instantiate the correct sub-component:

```typescript
// components/FileViewer.tsx (lines 78-89)
if (isImagePath(filePath))   return <ImageViewer … />;
if (isAudioPath(filePath))   return <AudioViewer … />;
if (isDocumentPreviewPath(filePath)) return <DocumentViewer … />;
return <TextFileViewer … />;

```

## Image Preview with Live Reload

The `ImageViewer` (starting at line 109 in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx)) renders bitmaps by fetching a signed URL from `getFileApiUrl(filePath, "read")`. To prevent caching stale data, the component maintains a `bust` state variable that appends a cache-buster query parameter to the image source.

A critical feature of the **pi-web file preview** system is real-time synchronization. Each viewer opens an `EventSource` connection to the `watch` endpoint. When the server emits a `change` event, the component increments `bust`, forcing the browser to reload the image. The implementation wraps the native `<img>` element with CSS constraints (`max-width: 100%`, `object-fit: contain`) and displays metadata such as dimensions and file size.

The server enforces a hard limit: images exceeding `IMAGE_PREVIEW_MAX_BYTES` (10 MiB) are rejected by the API endpoint defined in `app/api/files/[...path]/route.ts`.

## Audio Streaming and Metadata

For audio assets, the `AudioViewer` (lines 42-56) utilizes the same `read` endpoint but renders a native `<audio>` element with `controls` and `preload="metadata"`. When the browser fires the `loadedmetadata` event, the component extracts the duration and surfaces it in the toolbar.

Like images, audio previews support live synchronization via `EventSource`. The system does not impose a specific size cap for audio streams beyond the generic file-size checks performed by the `meta` endpoint.

## PDF and DOCX Document Rendering

The `DocumentViewer` (lines 58-71) handles compound documents through two distinct paths:

**PDF files** stream directly through the `read` endpoint and render inside an `<iframe>` pointing to the raw file URL. No sandbox restrictions are applied, allowing the browser's native PDF viewer to execute.

**DOCX files** require server-side conversion. The viewer requests `type="preview"` from `getFileApiUrl`, which triggers a conversion to HTML on the backend. Before fetching, the component checks the file metadata; if the size exceeds `DOCX_PREVIEW_MAX_BYTES` (10 MiB), it displays an error instead of attempting conversion. The resulting HTML loads into an `<iframe>` with an empty sandbox attribute to isolate the converted content.

## Usage Examples

To open an image with live-update capabilities:

```tsx
import { FileViewer } from "@/components/FileViewer";

<FileViewer
  filePath="/home/user/assets/photo.png"
  cwd="/home/user"
  sourceSessionId={sessionId}
/>

```

For DOCX files, the component automatically detects the extension and applies the size limit:

```tsx
<FileViewer
  filePath="reports/annual-report.docx"
  cwd="/home/user/project"
  sourceSessionId={sessionId}
/>

```

PDF files load directly without conversion overhead:

```tsx
<FileViewer
  filePath="static/brochure.pdf"
  cwd="/home/user/project"
  sourceSessionId={sessionId}
/>

```

## Summary

- **Centralized routing**: The `FileViewer` component in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx) delegates to specialized viewers based on [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts) predicates.
- **Size enforcement**: Images and DOCX files are capped at 10 MiB (`IMAGE_PREVIEW_MAX_BYTES` and `DOCX_PREVIEW_MAX_BYTES`).
- **Live sync**: All viewers utilize `EventSource` connections to the `watch` endpoint for real-time content updates.
- **Endpoint differentiation**: Images and PDFs use `type="read"`, while DOCX files require `type="preview"` for server-side HTML conversion.
- **Secure rendering**: Documents render in `<iframe>` elements, with DOCX previews sandboxed separately from native PDF viewer contexts.

## Frequently Asked Questions

### What is the maximum file size for file previews in pi-web?

Images and DOCX documents are limited to 10 MiB by the `IMAGE_PREVIEW_MAX_BYTES` and `DOCX_PREVIEW_MAX_BYTES` constants. Exceeding these limits triggers an error state in the `ImageViewer` or `DocumentViewer` components. Audio files and PDFs do not have explicit preview size limits beyond standard API constraints.

### How does pi-web synchronize preview content when a file changes?

The system establishes an `EventSource` connection to the `watch` endpoint upon mounting a viewer component. When the server broadcasts a `change` event, the viewer updates a `bust` state variable, appending a cache-busting query parameter to force a fresh fetch of the file content.

### Why do PDF and DOCX files use different API endpoints for previews?

PDFs render natively in the browser, so the `DocumentViewer` requests `type="read"` to stream the raw binary. DOCX files require server-side conversion to HTML, necessitating the `type="preview"` endpoint. This distinction allows pi-web to handle complex Office documents without client-side parsing libraries.

### What happens if a file type is not supported for preview?

When a file fails the `isImagePath`, `isAudioPath`, and `isDocumentPreviewPath` checks, the `FileViewer` falls back to `TextFileViewer`, which attempts to display the content as plain text or code. Binary files that cannot be interpreted as text will typically show encoding errors or empty states depending on the browser context.