Customization
KViewer provides several customization options through slots, component prefix configuration, and CSS.
Replace the Header Toolbar
Use the header slot to replace the default ViewerBar with a custom toolbar:
<template>
<div class="h-screen">
<KViewer :source="pdfUrl">
<template #header>
<div class="flex items-center gap-2 p-2 border-b">
<button @click="exportPdf">Download</button>
<span>Custom Toolbar</span>
</div>
</template>
</KViewer>
</div>
</template>
Custom menu items
If you only want to extend the burger menu rather than replace the whole header, pass a menuItems array. Items appear after the built-in Download / Detect-form-fields entries, separated by a divider.
Three item shapes are supported — see ViewerMenuItem. The host owns the rendered state, so toggle items stay in sync with whatever reactive source you bind to.
<script setup lang="ts">
import type { ViewerMenuItem } from 'kviewer'
const debugMode = ref(false)
const menuItems = computed<ViewerMenuItem[]>(() => [
{
type: 'button',
key: 'reset-zoom',
label: 'Reset zoom',
icon: 'i-lucide-rotate-ccw',
onSelect: () => { /* ... */ },
},
{
type: 'separator',
key: 'sep',
},
{
type: 'checkbox',
key: 'debug',
label: 'Debug overlay',
icon: 'i-lucide-bug',
checked: debugMode.value,
onUpdate: (v) => { debugMode.value = v },
},
])
</script>
<template>
<KViewer :source="pdfUrl" :menu-items="menuItems" />
</template>
Disabling menu items
Set disabled: true on a button or checkbox item to grey it out without removing it. The menu re-renders whenever menuItems changes, so derive disabled from your own state in a computed and the item enables and disables itself:
<script setup lang="ts">
import type { ViewerMenuItem } from 'kviewer'
const allSigned = ref(false)
const menuItems = computed<ViewerMenuItem[]>(() => [
{
type: 'button',
key: 'finalize',
label: 'Finalize envelope',
icon: 'i-lucide-send',
disabled: !allSigned.value,
onSelect: () => { /* submit */ },
},
])
</script>
<template>
<KViewer
:source="pdfUrl"
:menu-items="menuItems"
@signed-status-changed="({ allSigned: v }) => (allSigned = v)"
/>
</template>
Mutating an item inside a ref([...]) works too (menuItems.value[0].disabled = true). A plain, non-reactive array won't update after the first render.
disabled only applies to your own menuItems. Built-in toolbar tools can't be disabled — leave them out of the tools prop instead. Selection popover items support the same disabled flag.
On KViewerTabs, menuItems is forwarded to every inner viewer:
<KViewerTabs :items="tabs" :menu-items="menuItems" />
menuItems is ignored when you supply a custom #header slot — the host owns rendering at that point.Custom selection popover items
Selecting an annotation — or a placed form field such as a signature field (with formEditMode or editablePlacedFields) — shows a floating popover with the built-in actions, ending in a delete button. Pass a selectionItems array to append your own buttons and selects.
Every callback receives the current selection as a context object (kind, annotations, field), and a visible(ctx) predicate scopes an item to one selection kind — see ViewerSelectionItem.
<script setup lang="ts">
import type { ViewerSelectionItem } from 'kviewer'
const selectionItems: ViewerSelectionItem[] = [
{
type: 'button',
key: 'duplicate-field',
label: 'Duplicate field',
icon: 'i-lucide-copy',
onSelect: (ctx) => duplicateField(ctx.field!),
visible: (ctx) => ctx.kind === 'field',
},
{
type: 'select',
key: 'importance',
label: 'Importance',
items: [
{ label: 'Low', value: 'low' },
{ label: 'High', value: 'high' },
],
value: (ctx) => importanceOf(ctx),
onUpdate: (value, ctx) => setImportance(ctx, value),
},
]
</script>
<template>
<KViewer :source="pdfUrl" :selection-items="selectionItems" />
</template>
On KViewerTabs, selectionItems is forwarded to every inner viewer, like menuItems.
Add a Footer
Use the footer slot to add content below the viewer:
<KViewer :source="pdfUrl">
<template #footer>
<div class="flex items-center justify-between p-2 border-t">
<span>Page info</span>
<button @click="saveDraft">Save Draft</button>
</div>
</template>
</KViewer>
Customize KViewerTabs Slots
KViewerTabs provides additional slots for the tab bar:
<KViewerTabs :items="tabs">
<!-- Content before the tab list -->
<template #tabs-leading>
<span class="px-3 font-semibold">My Documents</span>
</template>
<!-- Content after the tab list -->
<template #tabs-trailing>
<button @click="addTab">+ New Tab</button>
</template>
<!-- Custom header for each viewer (receives tab context) -->
<template #header="{ tab }">
<div class="p-2 border-b">
Viewing: {{ tab.label }}
</div>
</template>
<!-- Custom footer for each viewer -->
<template #footer>
<div class="p-2 border-t text-sm text-muted">
Custom footer content
</div>
</template>
<!-- Shown when all tabs are closed -->
<template #empty>
<div class="flex flex-col items-center justify-center h-full">
<p>No documents open</p>
</div>
</template>
</KViewerTabs>
Change the Component Prefix
Customize the component prefix in your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['kviewer'],
ssr: false,
kviewer: {
prefix: 'Pdf',
},
})
With prefix: 'Pdf', components are registered as:
PdfViewerinstead ofKViewerPdfViewerTabsinstead ofKViewerTabs
Styling
KViewer uses Tailwind CSS for styling and auto-includes required CSS files:
pdfjs-dist/web/pdf_viewer.cssfor the text layerkviewer.cssfor annotation cursor styles
The viewer components follow your project's Tailwind theme. To customize annotation colors or toolbar appearance, override the relevant CSS custom properties or use Tailwind utilities in your slot content.