0
# Navigation Components
1
2
Navigation components for PDF outline (table of contents) and thumbnail displays to enhance user experience and provide quick document navigation.
3
4
## Capabilities
5
6
### Outline Component
7
8
Displays PDF document outline (table of contents/bookmarks) with hierarchical navigation structure.
9
10
```typescript { .api }
11
/**
12
* Displays an outline (table of contents) for the PDF document
13
* Should be placed inside <Document /> or passed explicit pdf prop
14
* @param props - Outline configuration and event handlers
15
* @returns React element rendering the outline or null if no outline exists
16
*/
17
function Outline(props: OutlineProps): React.ReactElement | null;
18
19
interface OutlineProps {
20
/** Function called when outline item is clicked */
21
onItemClick?: (args: OnItemClickArgs) => void;
22
/** Function called when outline loads successfully */
23
onLoadSuccess?: (outline: PDFOutline | null) => void;
24
/** Function called when outline fails to load */
25
onLoadError?: (error: Error) => void;
26
/** Class names for styling */
27
className?: ClassName;
28
/** React ref for the outline container div */
29
inputRef?: React.Ref<HTMLDivElement>;
30
/** PDF document proxy (usually provided by Document context) */
31
pdf?: PDFDocumentProxy | false;
32
}
33
34
type PDFOutline = OutlineNode[] | null;
35
36
interface OutlineNode {
37
/** Title of the outline item */
38
title: string;
39
/** Destination reference */
40
dest: Dest | null;
41
/** Child outline items */
42
items: OutlineNode[];
43
/** Whether the item is bold */
44
bold?: boolean;
45
/** Whether the item is italic */
46
italic?: boolean;
47
/** Item color in RGB */
48
color?: number[];
49
}
50
51
interface OnItemClickArgs {
52
/** Destination reference for navigation */
53
dest?: Dest;
54
/** Zero-based page index */
55
pageIndex: number;
56
/** One-based page number */
57
pageNumber: number;
58
}
59
```
60
61
**Usage Examples:**
62
63
```typescript
64
import { Document, Page, Outline } from "react-pdf";
65
66
// Basic outline display
67
function PDFWithOutline() {
68
return (
69
<div style={{ display: 'flex' }}>
70
<Document file="document-with-toc.pdf">
71
<div style={{ width: '200px', padding: '10px' }}>
72
<h3>Table of Contents</h3>
73
<Outline />
74
</div>
75
<div style={{ flex: 1 }}>
76
<Page pageNumber={1} />
77
</div>
78
</Document>
79
</div>
80
);
81
}
82
83
// Outline with custom navigation handling
84
function CustomOutlineNavigation() {
85
const [currentPage, setCurrentPage] = useState(1);
86
87
const handleOutlineClick = ({ pageNumber }) => {
88
setCurrentPage(pageNumber);
89
console.log('Navigating to page', pageNumber);
90
};
91
92
return (
93
<Document file="sample.pdf">
94
<Outline
95
onItemClick={handleOutlineClick}
96
onLoadSuccess={(outline) => {
97
if (outline) {
98
console.log('Outline loaded with', outline.length, 'items');
99
} else {
100
console.log('No outline available');
101
}
102
}}
103
/>
104
<Page pageNumber={currentPage} />
105
</Document>
106
);
107
}
108
109
// Styled outline
110
function StyledOutline() {
111
return (
112
<Document file="sample.pdf">
113
<Outline
114
className="custom-outline border p-4"
115
onLoadError={(error) => {
116
console.error('Failed to load outline:', error);
117
}}
118
/>
119
<Page pageNumber={1} />
120
</Document>
121
);
122
}
123
```
124
125
### Thumbnail Component
126
127
Displays page thumbnails for quick navigation and document overview.
128
129
```typescript { .api }
130
/**
131
* Displays a thumbnail of a page for navigation purposes
132
* Does not render text or annotation layers for performance
133
* Should be placed inside <Document /> or passed explicit pdf prop
134
* @param props - Thumbnail configuration extending Page props
135
* @returns React element rendering the page thumbnail
136
*/
137
function Thumbnail(props: ThumbnailProps): React.ReactElement;
138
139
interface ThumbnailProps extends Omit<PageProps,
140
| 'className'
141
| 'customTextRenderer'
142
| 'onGetAnnotationsError'
143
| 'onGetAnnotationsSuccess'
144
| 'onGetTextError'
145
| 'onGetTextSuccess'
146
| 'onRenderAnnotationLayerError'
147
| 'onRenderAnnotationLayerSuccess'
148
| 'onRenderTextLayerError'
149
| 'onRenderTextLayerSuccess'
150
| 'renderAnnotationLayer'
151
| 'renderForms'
152
| 'renderTextLayer'
153
> {
154
/** Class names for styling the thumbnail */
155
className?: ClassName;
156
/** Function called when thumbnail is clicked */
157
onItemClick?: (args: OnItemClickArgs) => void;
158
}
159
```
160
161
**Usage Examples:**
162
163
```typescript
164
import { Document, Page, Thumbnail } from "react-pdf";
165
166
// Basic thumbnail grid
167
function ThumbnailGrid() {
168
const [numPages, setNumPages] = useState(null);
169
const [currentPage, setCurrentPage] = useState(1);
170
171
return (
172
<Document
173
file="sample.pdf"
174
onLoadSuccess={({ numPages }) => setNumPages(numPages)}
175
>
176
<div style={{ display: 'flex' }}>
177
<div style={{ width: '200px', maxHeight: '600px', overflow: 'auto' }}>
178
{Array.from({ length: numPages }, (_, index) => (
179
<Thumbnail
180
key={`thumb_${index + 1}`}
181
pageNumber={index + 1}
182
width={150}
183
className={`thumbnail ${currentPage === index + 1 ? 'active' : ''}`}
184
onItemClick={({ pageNumber }) => setCurrentPage(pageNumber)}
185
/>
186
))}
187
</div>
188
<div style={{ flex: 1 }}>
189
<Page pageNumber={currentPage} />
190
</div>
191
</div>
192
</Document>
193
);
194
}
195
196
// Thumbnail carousel
197
function ThumbnailCarousel() {
198
const [numPages, setNumPages] = useState(null);
199
const [currentPage, setCurrentPage] = useState(1);
200
201
return (
202
<Document
203
file="sample.pdf"
204
onLoadSuccess={({ numPages }) => setNumPages(numPages)}
205
>
206
<div>
207
<Page pageNumber={currentPage} scale={1.2} />
208
<div style={{
209
display: 'flex',
210
overflowX: 'auto',
211
padding: '10px',
212
gap: '10px'
213
}}>
214
{Array.from({ length: numPages }, (_, index) => (
215
<Thumbnail
216
key={`thumb_${index + 1}`}
217
pageNumber={index + 1}
218
height={100}
219
className={`thumbnail-small ${
220
currentPage === index + 1 ? 'active' : ''
221
}`}
222
onItemClick={({ pageNumber }) => setCurrentPage(pageNumber)}
223
/>
224
))}
225
</div>
226
</div>
227
</Document>
228
);
229
}
230
231
// Thumbnail with page numbers
232
function ThumbnailWithNumbers() {
233
return (
234
<Document file="sample.pdf">
235
{Array.from({ length: 5 }, (_, index) => (
236
<div key={index} className="thumbnail-container">
237
<Thumbnail
238
pageNumber={index + 1}
239
width={120}
240
scale={0.3}
241
/>
242
<div className="page-number">Page {index + 1}</div>
243
</div>
244
))}
245
</Document>
246
);
247
}
248
```
249
250
### Navigation Event Handling
251
252
Centralized navigation handling through Document component for consistent behavior.
253
254
```typescript { .api }
255
interface NavigationHandling {
256
/** Central click handler for all navigation components */
257
onItemClick?: (args: OnItemClickArgs) => void;
258
}
259
260
interface OnItemClickArgs {
261
/** Destination object for internal links */
262
dest?: Dest;
263
/** Zero-based page index */
264
pageIndex: number;
265
/** One-based page number */
266
pageNumber: number;
267
}
268
269
type Dest = Promise<ResolvedDest> | ResolvedDest | string | null;
270
type ResolvedDest = (RefProxy | number)[];
271
```
272
273
**Usage Examples:**
274
275
```typescript
276
// Centralized navigation handling
277
function PDFNavigator() {
278
const [currentPage, setCurrentPage] = useState(1);
279
const [numPages, setNumPages] = useState(null);
280
281
const handleNavigation = ({ pageNumber, dest }) => {
282
setCurrentPage(pageNumber);
283
284
// Handle different destination types
285
if (dest && typeof dest === 'string') {
286
// Named destination
287
console.log('Navigating to named destination:', dest);
288
} else if (dest && Array.isArray(dest)) {
289
// Explicit destination with coordinates
290
console.log('Navigating to coordinates:', dest);
291
}
292
};
293
294
return (
295
<Document
296
file="sample.pdf"
297
onLoadSuccess={({ numPages }) => setNumPages(numPages)}
298
onItemClick={handleNavigation}
299
>
300
<div style={{ display: 'flex' }}>
301
{/* Outline navigation */}
302
<div style={{ width: '250px' }}>
303
<h3>Contents</h3>
304
<Outline />
305
306
<h3>Pages</h3>
307
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
308
{Array.from({ length: numPages }, (_, index) => (
309
<Thumbnail
310
key={index}
311
pageNumber={index + 1}
312
width={80}
313
className={currentPage === index + 1 ? 'active' : ''}
314
/>
315
))}
316
</div>
317
</div>
318
319
{/* Main page view */}
320
<div style={{ flex: 1 }}>
321
<Page pageNumber={currentPage} />
322
</div>
323
</div>
324
</Document>
325
);
326
}
327
328
// Navigation with history
329
function PDFWithHistory() {
330
const [pageHistory, setPageHistory] = useState([1]);
331
const [historyIndex, setHistoryIndex] = useState(0);
332
333
const navigateToPage = ({ pageNumber }) => {
334
const newHistory = pageHistory.slice(0, historyIndex + 1);
335
newHistory.push(pageNumber);
336
setPageHistory(newHistory);
337
setHistoryIndex(newHistory.length - 1);
338
};
339
340
const goBack = () => {
341
if (historyIndex > 0) {
342
setHistoryIndex(historyIndex - 1);
343
}
344
};
345
346
const goForward = () => {
347
if (historyIndex < pageHistory.length - 1) {
348
setHistoryIndex(historyIndex + 1);
349
}
350
};
351
352
const currentPage = pageHistory[historyIndex];
353
354
return (
355
<Document file="sample.pdf" onItemClick={navigateToPage}>
356
<div>
357
<div className="navigation-controls">
358
<button onClick={goBack} disabled={historyIndex === 0}>
359
Back
360
</button>
361
<button onClick={goForward} disabled={historyIndex === pageHistory.length - 1}>
362
Forward
363
</button>
364
<span>Page {currentPage}</span>
365
</div>
366
367
<div style={{ display: 'flex' }}>
368
<Outline />
369
<Page pageNumber={currentPage} />
370
</div>
371
</div>
372
</Document>
373
);
374
}
375
```
376
377
### Outline Structure
378
379
Understanding and working with hierarchical outline data.
380
381
```typescript { .api }
382
interface OutlineStructure {
383
/** Process nested outline items */
384
processOutline?: (outline: PDFOutline) => void;
385
/** Extract all destinations from outline */
386
extractDestinations?: (outline: PDFOutline) => Dest[];
387
/** Find outline item by destination */
388
findOutlineItem?: (outline: PDFOutline, dest: Dest) => OutlineNode | null;
389
}
390
```
391
392
**Usage Examples:**
393
394
```typescript
395
// Custom outline processor
396
function CustomOutlineProcessor() {
397
const [outlineData, setOutlineData] = useState(null);
398
399
const processOutline = (outline) => {
400
if (!outline) return;
401
402
// Flatten outline for search
403
const flatOutline = [];
404
405
const flatten = (items, level = 0) => {
406
items.forEach(item => {
407
flatOutline.push({ ...item, level });
408
if (item.items && item.items.length > 0) {
409
flatten(item.items, level + 1);
410
}
411
});
412
};
413
414
flatten(outline);
415
setOutlineData(flatOutline);
416
};
417
418
return (
419
<Document file="sample.pdf">
420
<Outline
421
onLoadSuccess={processOutline}
422
onItemClick={({ pageNumber, dest }) => {
423
const item = outlineData?.find(item => item.dest === dest);
424
console.log('Clicked outline item:', item?.title);
425
}}
426
/>
427
</Document>
428
);
429
}
430
431
// Outline search functionality
432
function SearchableOutline() {
433
const [outline, setOutline] = useState(null);
434
const [searchTerm, setSearchTerm] = useState('');
435
const [filteredOutline, setFilteredOutline] = useState(null);
436
437
useEffect(() => {
438
if (!outline || !searchTerm) {
439
setFilteredOutline(outline);
440
return;
441
}
442
443
const filterOutline = (items) => {
444
return items.filter(item => {
445
const matchesSearch = item.title.toLowerCase().includes(searchTerm.toLowerCase());
446
const hasMatchingChildren = item.items && filterOutline(item.items).length > 0;
447
448
if (matchesSearch || hasMatchingChildren) {
449
return {
450
...item,
451
items: hasMatchingChildren ? filterOutline(item.items) : item.items
452
};
453
}
454
return false;
455
}).filter(Boolean);
456
};
457
458
setFilteredOutline(filterOutline(outline));
459
}, [outline, searchTerm]);
460
461
return (
462
<Document file="sample.pdf">
463
<div>
464
<input
465
type="text"
466
placeholder="Search outline..."
467
value={searchTerm}
468
onChange={(e) => setSearchTerm(e.target.value)}
469
/>
470
<Outline
471
onLoadSuccess={setOutline}
472
// Note: This is conceptual - actual filtering would need custom rendering
473
/>
474
</div>
475
</Document>
476
);
477
}
478
```