A comprehensive PDF viewer component for Svelte 5 with interactive features like text selection, annotations, and flexible display modes.
npm install svelte-pdf
<script>
import { PDFViewer } from 'svelte-pdf';
let pdfFile = $state();
let currentPage = $state(1);
let scale = $state(1.0);
function handleTextSelection(selection) {
console.log('Selected:', selection.text);
}
</script>
<input
type="file"
accept=".pdf"
on:change={(e) => pdfFile = e.target.files[0]}
/>
{#if pdfFile}
<PDFViewer
src={pdfFile}
bind:page={currentPage}
bind:scale={scale}
showNavigation={true}
enableTextSelection={true}
onTextSelect={handleTextSelection}
/>
{/if}
Prop | Type | Default | Description |
---|---|---|---|
src |
PDFSource |
- | Required. PDF source (File, ArrayBuffer, or URL string) |
page |
number |
1 |
Current page number (bindable) |
scale |
number |
1.0 |
Current zoom scale (bindable) |
fitMode |
FitMode |
'none' |
How to fit PDF in container |
displayMode |
DisplayMode |
'single' |
Page display mode |
showNavigation |
boolean |
true |
Show navigation controls |
enableTextSelection |
boolean |
true |
Enable text selection |
theme |
Theme |
'light' |
UI theme |
containerClass |
string |
'' |
Additional CSS class for container |
canvasClass |
string |
'' |
Additional CSS class for canvas |
width |
number |
- | Fixed width (overrides fit modes) |
height |
number |
- | Fixed height (overrides fit modes) |
maxScale |
number |
3.0 |
Maximum zoom scale |
minScale |
number |
0.1 |
Minimum zoom scale |
Event | Type | Description |
---|---|---|
onLoad |
(event: { numPages: number; fingerprint: string }) => void |
Fired when PDF loads |
onError |
(event: { error: string }) => void |
Fired on error |
onPageChange |
(event: { page: number; total: number }) => void |
Fired when page changes |
onScaleChange |
(event: { scale: number }) => void |
Fired when zoom changes |
onTextSelect |
(selection: TextSelection) => void |
Fired when text is selected |
Configure event callbacks and interactive behaviors:
<PDFViewer
src={pdfFile}
onTextSelect={handleTextSelection}
onLoad={handleLoad}
onError={handleError}
onPageChange={handlePageChange}
onScaleChange={handleScaleChange}
zoomOptions={{
onZoom: handleZoom,
minScale: 0.5,
maxScale: 5.0,
step: 0.1
}}
navigationOptions={{
onPageChange: handlePageChange,
enableKeyboard: true,
enableMouseWheel: false
}}
annotationOptions={{
onAnnotate: handleAnnotation,
color: '#ff0000',
thickness: 2
}}
/>
type PDFSource = string | File | ArrayBuffer;
type DisplayMode = 'single' | 'continuous';
type FitMode = 'none' | 'width' | 'page' | 'auto';
type Theme = 'light' | 'dark';
interface TextSelection {
text: string;
pageNumber: number;
x: number;
y: number;
width: number;
height: number;
}
<script>
import { PDFViewer } from 'svelte-pdf';
let pdfUrl = $state('https://example.com/document.pdf');
</script>
<PDFViewer src={pdfUrl} />
<script>
import { PDFViewer } from 'svelte-pdf';
let pdfFile = $state();
let currentPage = $state(1);
let scale = $state(1.0);
let displayMode = $state('continuous');
let theme = $state('dark');
function handleTextSelection(selection) {
console.log('Selected:', selection.text);
}
function handlePageChange(event) {
console.log(`Page ${event.page} of ${event.total}`);
}
</script>
<PDFViewer
src={pdfFile}
bind:page={currentPage}
bind:scale={scale}
{displayMode}
{theme}
fitMode="width"
showNavigation={true}
enableTextSelection={true}
onTextSelect={handleTextSelection}
onPageChange={handlePageChange}
containerClass="my-pdf-viewer"
maxScale={5.0}
minScale={0.25}
/>
<script>
import { PDFViewer } from 'svelte-pdf';
let selectedTexts = $state([]);
function handleTextSelection(selection) {
selectedTexts = [...selectedTexts, {
id: Date.now(),
text: selection.text,
page: selection.pageNumber,
coordinates: {
x: selection.x,
y: selection.y,
width: selection.width,
height: selection.height
}
}];
}
</script>
<PDFViewer
src={pdfFile}
onTextSelect={handleTextSelection}
/>
{#if selectedTexts.length > 0}
<div class="selections">
<h3>Selected Text:</h3>
{#each selectedTexts as selection (selection.id)}
<div class="selection">
<strong>Page {selection.page}:</strong> {selection.text}
</div>
{/each}
</div>
{/if}
<script>
import { PDFViewer } from 'svelte-pdf';
let pdfViewer = $state();
let currentPage = $state(1);
let scale = $state(1.0);
let totalPages = $state(0);
function goToPage(pageNum) {
currentPage = Math.max(1, Math.min(pageNum, totalPages));
}
function zoomIn() {
scale = Math.min(scale * 1.2, 3.0);
}
function zoomOut() {
scale = Math.max(scale / 1.2, 0.1);
}
function handleLoad(event) {
totalPages = event.numPages;
}
</script>
<div class="controls">
<button onclick={() => goToPage(currentPage - 1)}>Previous</button>
<span>Page {currentPage} of {totalPages}</span>
<button onclick={() => goToPage(currentPage + 1)}>Next</button>
<button onclick={zoomOut}>Zoom Out</button>
<span>{Math.round(scale * 100)}%</span>
<button onclick={zoomIn}>Zoom In</button>
</div>
<PDFViewer
bind:this={pdfViewer}
src={pdfFile}
bind:page={currentPage}
bind:scale={scale}
onLoad={handleLoad}
showNavigation={false}
/>
Key | Action |
---|---|
← / Page Up |
Previous page |
→ / Page Down / Space |
Next page |
Home |
First page |
End |
Last page |
Ctrl/Cmd + + |
Zoom in |
Ctrl/Cmd + - |
Zoom out |
Ctrl/Cmd + 0 |
Reset zoom |
Ctrl/Cmd + Scroll |
Zoom with mouse wheel |
The component provides CSS custom properties for theming:
.pdf-viewer {
--nav-bg: rgba(255, 255, 255, 0.1);
--border-color: #ddd;
--button-bg: #fff;
--button-color: #333;
--button-hover-bg: #f0f0f0;
}
.pdf-viewer.dark {
--nav-bg: rgba(0, 0, 0, 0.1);
--border-color: #555;
--button-bg: #333;
--button-color: #fff;
--button-hover-bg: #444;
}
MIT License - see LICENSE file for details.
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
enableTextSelection
is true
displayMode="single"
for better performance with large documents