Multi-Tab Documents
KViewerTabs provides a tabbed interface for managing multiple PDF documents. Each tab runs its own KViewer instance with independent annotations, form fields, and view settings.
Basic Setup
<template>
<div class="h-screen">
<KViewerTabs :items="tabs" />
</div>
</template>
<script setup lang="ts">
const tabs = [
{ id: 'contract', label: 'Contract.pdf', source: '/contract.pdf' },
{ id: 'invoice', label: 'Invoice.pdf', source: '/invoice.pdf' },
{ id: 'report', label: 'Report.pdf', source: '/report.pdf' },
]
</script>
Tab Item Structure
Each tab item accepts these properties:
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique tab identifier |
label | string | Yes | Display name in tab bar |
source | string | Uint8Array | object | Yes | PDF source |
icon | string | No | Lucide icon name (default: i-lucide-file-text) |
closable | boolean | No | Allow closing this tab (default: true) |
viewMode | ViewMode | No | Override view mode for this tab |
zoom | number | No | Override zoom for this tab |
shapeDetection | boolean | No | Override shape detection for this tab |
Add Tabs Dynamically
Use the template ref to add new tabs at runtime:
<template>
<div class="h-screen">
<KViewerTabs ref="viewerTabs" :items="initialTabs">
<template #tabs-trailing>
<button @click="onAddTab">+ Add</button>
</template>
</KViewerTabs>
</div>
</template>
<script setup lang="ts">
const viewerTabs = ref()
const initialTabs = [
{ id: 'doc1', label: 'Document.pdf', source: '/document.pdf' },
]
function onAddTab() {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.pdf'
input.onchange = async () => {
const file = input.files?.[0]
if (!file) return
const source = new Uint8Array(await file.arrayBuffer())
viewerTabs.value?.addTab({ label: file.name, source })
}
input.click()
}
</script>
AddTabOptions
viewerTabs.value?.addTab(
{ label: 'New.pdf', source: pdfBytes },
{
index: 0, // Insert at position (default: end)
activate: true, // Switch to new tab (default: true)
},
)
Remove Tabs
viewerTabs.value?.removeTab('doc1')
Use the minTabs prop to prevent closing tabs below a threshold:
<KViewerTabs :items="tabs" :min-tabs="1" />
Access Individual Viewers
Get the underlying KViewer instance for a specific tab to call its methods:
const viewer = viewerTabs.value?.getViewer('contract')
const annotations = viewer?.getAnnotations()
const bytes = await viewer?.exportPdf({ flatten: true })
Listen to Tab Events
<KViewerTabs
:items="tabs"
@update:active-tab="onTabChange"
@tab-added="onTabAdded"
@tab-close="onTabClose"
@tab-removed="onTabRemoved"
/>
| Event | Payload | Description |
|---|---|---|
update:activeTab | string (tab ID) | Active tab changed |
tab-added | ViewerTabItem | New tab was created |
tab-close | string (tab ID) | Tab is about to close |
tab-removed | string (tab ID) | Tab was removed |
Customize the Tab Bar
Use slots to add content before or after the tab list:
<KViewerTabs :items="tabs">
<template #tabs-leading>
<span class="px-2 font-bold">Documents</span>
</template>
<template #tabs-trailing>
<button @click="onAddTab">+</button>
</template>
<template #empty>
<div class="flex items-center justify-center h-full">
<p>No documents open</p>
<button @click="onAddTab">Open a document</button>
</div>
</template>
</KViewerTabs>
Annotation Persistence
When switching between tabs, annotations are automatically saved and restored. You don't need to manually call getAnnotations() or importAnnotations() -- the tab manager handles this internally.
Forward Props to All Viewers
Props like stamps, userName, signatureHandlers, textLayer, readonly, and shapeDetection are forwarded to all tab viewers:
<KViewerTabs
:items="tabs"
:stamps="stamps"
user-name="Jane Doe"
text-layer
readonly
/>
Individual tabs can override viewMode, zoom, and shapeDetection through their ViewerTabItem definition.