packages/desktop/apps/electron/resources/docs/pdf-preview.md
This guide covers how to display PDF documents inline using pdf-preview code blocks.
The pdf-preview block renders PDF files inline in chat messages — showing the first page with an expand button for full multi-page navigation in a fullscreen overlay.
| Format | Best For | Rendering |
|---|---|---|
pdf-preview block | PDF documents, reports, invoices | First page inline, full navigation in fullscreen |
html-preview block | Emails, newsletters, styled HTML | Sandboxed iframe with full CSS |
image-preview block | Screenshots, captures, visual diffs | Inline fit-to-container + fullscreen viewer |
datatable/spreadsheet | Structured data, tables | Interactive sortable/filterable tables |
Key principle: Unlike html-preview which needs transform_data to extract HTML, PDFs are already files on disk. Just reference the file path directly — no extraction step needed.
Use pdf-preview when:
Do NOT use pdf-preview when:
datatable or spreadsheet insteadhtml-preview instead```pdf-preview
{
"src": "/absolute/path/to/file.pdf",
"title": "Q4 Financial Report"
}
```
When you have multiple related PDFs (e.g., quarterly reports, contract versions), use the items array. A tab bar appears below the header for switching between items.
```pdf-preview
{
"title": "Quarterly Reports",
"items": [
{ "src": "/path/to/q1-report.pdf", "label": "Q1" },
{ "src": "/path/to/q2-report.pdf", "label": "Q2" },
{ "src": "/path/to/q3-report.pdf", "label": "Q3" }
]
}
```
Content loads lazily on tab switch and is cached once loaded.
| Field | Required | Type | Description |
|---|---|---|---|
src | Yes* | string | Absolute path to the PDF file on disk (single item mode) |
title | No | string | Display title shown in the header bar (defaults to "PDF Preview") |
items | Yes* | array | Array of items with src and optional label (multi-item mode) |
items[].src | Yes | string | Absolute path to the PDF file |
items[].label | No | string | Tab label (defaults to "Item 1", "Item 2", etc.) |
*Either src (single) or items (multiple) is required. If both are present, items takes precedence.
Important: The src path must be an absolute path. Use the exact path from tool results or construct one using known directory paths.
When the Read tool reads a PDF, the file already exists on disk. Reference it directly:
```pdf-preview
{
"src": "/Users/john/Documents/report.pdf",
"title": "Annual Report 2025"
}
```
When a tool downloads a PDF (e.g., from an API response that returns binary data):
pdf-preview block```pdf-preview
{
"src": "/absolute/path/to/downloaded/invoice.pdf",
"title": "Invoice #12345"
}
```
When running a Python script that generates a PDF (e.g., with reportlab, fpdf2, or weasyprint):
# Example: generate PDF with fpdf2
from fpdf import FPDF
import sys
pdf = FPDF()
pdf.add_page()
pdf.set_font('Helvetica', size=16)
pdf.cell(text='Hello World')
pdf.output(sys.argv[-1])
Call via transform_data, then reference the output:
```pdf-preview
{
"src": "/absolute/path/from/transform_data/report.pdf",
"title": "Generated Report"
}
```
When the user references a PDF they have locally:
```pdf-preview
{
"src": "/Users/john/Downloads/contract.pdf",
"title": "Service Agreement"
}
```
Uint8Array) via IPC — efficient even for large filesThe Read tool already supports reading PDFs with the pages parameter. Use pdf-preview for visual rendering and Read for text extraction:
| Goal | Tool |
|---|---|
| See the PDF visually | pdf-preview code block |
| Extract text content | Read tool with pages parameter |
| Both | Use Read to extract text, then show pdf-preview for visual reference |
Does the user want to SEE the document?
→ YES: Use pdf-preview (visual fidelity matters)
→ NO: Extract text with Read tool and present as markdown
Is the content already a PDF file on disk?
→ YES: Use pdf-preview with direct file path
→ NO: Is it HTML? → Use html-preview
Is it data? → Use datatable/spreadsheet
Is it binary from an API? → Save to file first, then pdf-preview
"src" path must be an absolute path — not relative