# How Pi Web's File Viewer Handles Images, PDFs, and DOCX Files

> Discover how Pi Web's FileViewer component displays images, PDFs, and DOCX files. Learn about its efficient file type routing and specialized viewers for a seamless experience.

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

---

**Pi Web's `FileViewer` component in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx) routes files to specialized viewers—`ImageViewer`, `DocumentViewer`, or `TextFileViewer`—based on type detection via helper functions from [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts).**

Pi Web implements a unified file viewing system that automatically detects file types and renders them with appropriate viewers. The **file type handling** logic lives in two core modules: the dispatching component and a shared type-detection library. This architecture keeps rendering logic separated while ensuring consistent URL generation and preview behavior across all file formats.

## File Type Detection Architecture

All **file type detection** predicates reside in [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts). These pure functions examine file extensions and map them to MIME-based categories:

- `isImagePath()` — validates against known image extensions【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L63-L66】
- `isAudioPath()` — handles audio file detection【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L67-L69】
- `isDocumentPreviewPath()` — identifies PDF and DOCX files via `documentPreviewKind()`【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L71-L73】

These helpers are designed for reuse across the UI. The file explorer and other components can import the same functions without duplicating extension-to-MIME mapping logic.

## Dispatching to Specialized Viewers

The `FileViewer` component in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx) implements a simple cascade to select the correct renderer:

```tsx
if (isImagePath(filePath)) {
  return <ImageViewer … />;
}
if (isAudioPath(filePath)) {
  return <AudioViewer … />;
}
if (isDocumentPreviewPath(filePath)) {
  return <DocumentViewer … />;
}
return <TextFileViewer … />;

```

This branching occurs at lines 35–44【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】. Each viewer receives the same props—`filePath`, `cwd`, `sourceSessionId`, and `watchEnabled`—ensuring consistent behavior regardless of file type.

## How Pi Web's File Viewer Handles Images

The `ImageViewer` renders images through the `/api/files` endpoint using URL construction from `getFileApiUrl(..., "read")`【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L6-L10】.

Key implementation details:

- **Live synchronization**: The component optionally opens an `EventSource` to watch for file changes via the `watch` endpoint—useful for dynamically updating images or log visualizations【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L67-L82】
- **Aspect ratio preservation**: Images render with `objectFit: "contain"` to prevent distortion
- **Status indication**: A badge displays whether the image is actively syncing or static【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L31-L46】

```tsx
<ImageViewer
  filePath="/home/user/screenshots/dashboard.png"
  cwd="/home/user"
  sourceSessionId={sessionId}
  watchEnabled={true}  // Enables live reload on file changes
/>

```

## How Pi Web's File Viewer Handles PDFs and DOCX Files

The `DocumentViewer` handles both formats through the same component but uses different API endpoints depending on the file extension【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L59-L64】.

### PDF Rendering

PDFs stream directly from the `read` endpoint. The raw binary renders natively in the browser via an `<iframe>`【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L9-L15】.

### DOCX Rendering

DOCX files require server-side processing:

- The **preview endpoint** returns rendered HTML rather than the raw binary
- The iframe receives this HTML in a sandboxed environment for security isolation
- A **10 MiB size limit** (`DOCX_PREVIEW_MAX_BYTES`) prevents server overload—exceeding this triggers an error message instead of rendering【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L80-L84】

```tsx
<FileViewer
  filePath="/home/user/reports/annual-review.docx"
  cwd="/home/user/reports"
  sourceSessionId={sessionId}
  watchEnabled={false}  // DOCX previews don't support live sync
/>

```

## Shared URL Generation Infrastructure

All viewers rely on `getFileApiUrl()` defined in the same file【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L7-L19】. This helper supports four endpoint types:

| Endpoint Type | Purpose |
|-------------|---------|
| `read` | Returns raw file content (images, PDFs, text files) |
| `preview` | Returns server-rendered HTML (DOCX files) |
| `meta` | File metadata for headers and properties |
| `watch` | EventSource for live change notifications |

Manual URL construction example:

```ts
import { getFileApiUrl } from '@/components/FileViewer';

const previewUrl = getFileApiUrl(
  '/home/user/documents/requirements.docx',
  'preview',
  sessionId,
  { v: 1 }  // Cache-busting version parameter
);
// Result: /api/files?path=...&op=preview&session=...&v=1

```

## File Path Utilities

The [`lib/file-paths.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-paths.ts) module encodes paths for API safety and resolves relative paths for display. This separation keeps the viewer components focused on rendering rather than path manipulation.

## Summary

- **Type detection** occurs in [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts) via extension-based predicates (`isImagePath`, `isDocumentPreviewPath`)【/cache/repos/github.com/agegr/pi-web/main/lib/file-types.ts#L63-L73】
- **Component dispatch** in [`components/FileViewer.tsx`](https://github.com/agegr/pi-web/blob/main/components/FileViewer.tsx) routes files to `ImageViewer`, `DocumentViewer`, `AudioViewer`, or `TextFileViewer`【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】
- **Images** use the `read` endpoint with optional EventSource live sync【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L67-L82】
- **PDFs** render natively via the `read` endpoint in a sandboxed iframe
- **DOCX files** convert to HTML through the `preview` endpoint with a 10 MiB size limit【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L80-L84】
- **Unified URL builder** `getFileApiUrl()` supports `read`, `preview`, `meta`, and `watch` operations【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L7-L19】

## Frequently Asked Questions

### How does Pi Web determine which viewer to use for a file?

Pi Web checks file extensions in sequence using helper functions from [`lib/file-types.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-types.ts). `isImagePath()` tests against known image MIME mappings, `isAudioPath()` handles audio, and `isDocumentPreviewPath()` identifies PDF and DOCX files. The first matching predicate determines which specialized viewer component renders the file.

### Can Pi Web preview DOCX files without Microsoft Office installed?

Yes. The server-side `preview` endpoint converts DOCX to HTML before streaming it to the browser. The `DocumentViewer` receives this HTML in a sandboxed iframe. No client-side Office software is required, though DOCX files exceeding 10 MiB will not preview and instead display an error.

### Does Pi Web support live reloading for images?

Yes. When `watchEnabled={true}` is passed to `FileViewer`, the `ImageViewer` subscribes to file changes via an EventSource connection to the `watch` endpoint. This automatically refreshes the image without manual page reloads—particularly useful for dashboards, log visualizations, or screenshots from running processes.

### What happens if a file type doesn't match any known category?

Files that fail `isImagePath()`, `isAudioPath()`, and `isDocumentPreviewPath()` fall through to `TextFileViewer`, which renders the raw content as plain text【/cache/repos/github.com/agegr/pi-web/main/components/FileViewer.tsx#L35-L44】. This provides a safe fallback for source code, configuration files, and other text formats without requiring explicit type registration.