Vue Components
inklayer-vue provides two top-level components: PdfViewer and PdfAnnotator. Built on Vue 3 Composition API, using Pinia for state management, integrating shadcn-vue UI components.
🚀 Try It Online
🎯 Live Demo
Explore the full capabilities of InkLayer, including PDF Viewer and PDF Annotator.
👉 Open Live Demo💻 StackBlitz
Run and edit the Vue Starter directly in your browser—no local setup required.
👉 Open Vue Starter in StackBlitzInstallation
npm install inklayer-vue
Plugin Registration (Required)
You must register the InkLayer Vue plugin before using any components:
import { createApp } from 'vue'
import { inklayerVuePlugin } from 'inklayer-vue'
import App from './App.vue'
const app = createApp(App)
app.use(inklayerVuePlugin)
app.mount('#app')
Basic Import
// Components
import { PdfViewer, PdfAnnotator } from 'inklayer-vue'
import 'inklayer-vue/style'
// Types
import type {
PdfViewerProps,
PdfAnnotatorProps,
User,
Annotation,
AnnotationPermissions,
} from 'inklayer-vue'
PdfViewer
Lightweight PDF viewer without annotation features.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| showTextLayer | boolean | true | Show text selection layer |
| showAnnotations | boolean | false | Show existing annotations |
| defaultActiveSidebarKey | string | null | null | Default expanded sidebar panel |
| toolbar | Component | ((context: PdfViewerContextValue) => VueNode) | — | Custom toolbar |
| sidebar | SidebarPanel[] | — | Custom sidebar panels |
| actions | Component | ((context: PdfViewerContextValue) => VueNode) | — | Custom actions area |
| onDocumentLoaded | (pdfViewer: PDFViewer | null) => void | — | PDF loading complete callback |
| onEventBusReady | (eventBus: EventBus | null) => void | — | PDF.js EventBus ready callback |
Slots (PdfViewer)
| Slot Name | Slot Props | Description |
|---|---|---|
actions | context: PdfViewerContextValue | Custom actions area |
toolbar | context: PdfViewerContextValue | Completely replace toolbar |
sidebar-search-sidebar | context: PdfViewerContextValue | Custom search sidebar panel |
sidebar-${key} | context: PdfViewerContextValue | Dynamic slot, key comes from sidebar prop config |
Viewer Full Example
<script setup lang="ts">
import { ref } from 'vue'
import { PdfViewer } from 'inklayer-vue'
import 'inklayer-vue/style'
function onEventBusReady(eventBus: any) {
eventBus?.on('page-rendered', (e: any) => console.log('Page:', e.pageNumber))
}
function onDocumentLoaded(pdfViewer: any) {
console.log('Document loaded:', pdfViewer)
}
</script>
<template>
<PdfViewer
:enable-range="false"
title="PDF VIEWER CUSTOM"
:url="pdfUrl"
:layout-style="{ width: '100vw', height: '96vh' }"
:show-text-layer="false"
:show-annotations="true"
:default-active-sidebar-key="null"
:sidebar="customSidebar"
@event-bus-ready="onEventBusReady"
@document-loaded="onDocumentLoaded"
>
<template #actions="{ context }">
<button @click="context.print()">Print</button>
<button @click="context.download('test')">Download</button>
</template>
<template #toolbar="{ context }">
<button @click="context.toggleSidebar()">Toggle</button>
</template>
<template #sidebar-sidebar-1="{ context }">
<div>Custom Panel 1</div>
</template>
</PdfViewer>
</template>
PdfAnnotator
Full-featured PDF annotation component with built-in toolbar, annotation editing, and sidebar.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| url | string | URL | — | PDF file URL |
| data | string | number[] | ArrayBuffer | Uint8Array | Uint16Array | Uint32Array | — | PDF binary data |
| theme | See theme color table below | 'violet' | UI theme color |
| title | string | 'PDF ANNOTATOR' | Annotator title |
| locale | 'zh-CN' | 'en-US' | 'zh-CN' | UI language |
| initialScale | 'auto' | 'page-actual' | 'page-fit' | 'page-width' | number | 'auto' | Initial zoom scale |
| layoutStyle | CSSProperties | { width: '100vw', height: '100vh' } | Container styles (Vue has default) |
| user | { id: string, name: string } | { id: 'null', name: 'unknown' } | Current user info |
| annotationPermissions | AnnotationPermissions | { mode: 'unrestricted' } | Collaboration permissions for create, edit, transform, status, delete, and reply actions |
| enableNativeAnnotations | boolean | false | Enable PDF.js native annotations |
| enableRange | boolean | 'auto' | 'auto' | PDF range loading mode |
| defaultOptions | Partial<PdfAnnotatorOptions> | — | Default annotation options |
| initialAnnotations | Annotation[] | [] | Initial annotation data |
| defaultShowAnnotationAuthorLabels | boolean | false | Whether all annotation author labels are visible on initialization; users can still toggle them from the toolbar or with a shortcut |
| defaultShowAnnotationsSidebar | boolean | false | Whether annotation sidebar is expanded by default |
Theme Color Options
| Color Value | Description |
|---|---|
gray | Gray |
gold | Gold |
bronze | Bronze |
brown | Brown |
yellow | Yellow |
amber | Amber |
orange | Orange |
tomato | Tomato |
red | Red |
ruby | Ruby |
crimson | Crimson |
pink | Pink |
plum | Plum |
purple | Purple |
violet | Violet (default) |
iris | Iris |
indigo | Indigo |
blue | Blue |
cyan | Cyan |
teal | Teal |
jade | Jade |
green | Green |
grass | Grass |
lime | Lime |
mint | Mint |
sky | Sky |
Collaboration Permissions
Vue and React share the same permission rules. Pass the application’s current user through user, then enable ownership control with annotationPermissions.
<script setup lang="ts">
import { PdfAnnotator } from 'inklayer-vue'
import type { AnnotationPermissions, User } from 'inklayer-vue'
const currentUser: User = { id: 'alice', name: 'Alice' }
const permissions: AnnotationPermissions = {
mode: 'owner-only',
can: ({ currentUser }) =>
currentUser?.id === 'admin' ? true : undefined,
}
</script>
<template>
<PdfAnnotator
url="/document.pdf"
:user="currentUser"
:annotation-permissions="permissions"
default-show-annotation-author-labels
:layout-style="{ height: '100vh' }"
/>
</template>
For a fully read-only annotator, pass :annotation-permissions="{ can: () => false }". See Collaboration Permissions for the full action matrix, business-rule overrides, and security boundary.
Events
React → Vue event mapping: React’s
onXxxprops correspond to Vue’s@xxxevents (lowercase). For example:onSave→@save,onLoad→@load,onAnnotationAdded→@annotation-added.
| Event | Payload | Trigger |
|---|---|---|
| save | (annotations: Annotation[]) | User clicks save |
| load | () | PDF loading complete |
| annotation-added | (annotation: Annotation) | New annotation created |
| annotation-deleted | (id: string) | Annotation deleted |
| annotation-selected | (annotation: Annotation | null, isClick: boolean) | Annotation selected/deselected |
| annotation-updated | (annotation: Annotation) | Annotation updated |
Slots (PdfAnnotator)
| Slot Name | Slot Props | Description |
|---|---|---|
actions | { onSave, getAnnotations, exportToExcel, exportToPdf } | Custom actions area, rendered on the right side of toolbar |
Exposed Methods
Call exposed methods via ref:
| Method | Description |
|---|---|
save() | Trigger save operation |
getAnnotations() | Get all current annotation data |
Full Example: Annotator with Persistence and Slots
<script setup lang="ts">
import { ref } from 'vue'
import { PdfAnnotator } from 'inklayer-vue'
import 'inklayer-vue/style'
import type { Annotation } from 'inklayer-vue'
const annotatorRef = ref<InstanceType<typeof PdfAnnotator>>()
const annotations = ref<Annotation[]>([])
function handleSave(data: Annotation[]) {
annotations.value = data
// Send to backend
fetch('/api/annotations', {
method: 'POST',
body: JSON.stringify(data),
})
}
</script>
<template>
<PdfAnnotator
ref="annotatorRef"
url="/document.pdf"
locale="en-US"
:layout-style="{ height: '96vh' }"
:enable-range="true"
:default-show-annotations-sidebar="true"
:user="{ id: '9527', name: 'Lao Mai' }"
:enable-native-annotations="true"
:initial-annotations="[]"
:default-options="defaultOptions"
@save="handleSave"
@load="() => console.log('PDF Loaded')"
@annotation-added="(a) => console.log('Added:', a.id)"
@annotation-deleted="(id) => console.log('Deleted:', id)"
@annotation-updated="(a) => console.log('Updated:', a.id)"
@annotation-selected="(a, isClick) => console.log('Selected:', a?.id, isClick)"
>
<template #actions="{ onSave, getAnnotations, exportToExcel, exportToPdf }">
<button @click="onSave()">Save</button>
<button @click="console.log(getAnnotations())">Get Annotations</button>
<button @click="exportToExcel('my-export')">Export Excel</button>
<button @click="exportToPdf('my-export')">Export PDF</button>
</template>
</PdfAnnotator>
</template>
SidebarPanel Type
The sidebar prop accepts SidebarPanel[], defining custom sidebar panels:
interface SidebarPanel {
key: string // Panel unique key
title: string // Panel title
icon: Vue.VNode // Panel icon
render: (context: PdfViewerContextValue) => Vue.VNode // Panel content render function
}
PdfViewerContextValue Type
The function form of actions, toolbar, sidebar receives a PdfViewerContextValue object:
interface PdfViewerContextValue {
pdfViewer: PDFViewer | null // PDFViewer instance
currentPage: number // Current page number (0-based)
totalPages: number // Total pages
scale: number // Current zoom scale
print: () => void // Print
download: (filename?: string) => void // Download
toggleSidebar: () => void // Toggle sidebar
scrollPageIntoView: (opts: { pageNumber: number }) => void // Scroll to page
}
PdfAnnotatorOptions Configuration
Customize default annotation behavior via the defaultOptions prop:
interface PdfAnnotatorOptions {
colors?: string[] // Available color list
signature?: {
colors?: string[] // Signature color options
type?: 'Draw' | 'Enter' | 'Upload' // Signature type (note capital first letter)
maxSize?: number // Max signature image file size (bytes)
accept?: string // Accepted image formats
defaultSignature?: string[] // Default signature list
defaultFont?: { // Default font list
label: string
value: string
external?: boolean
url?: string // External font URL
}[]
}
stamp?: {
maxSize?: number // Stamp image max file size
accept?: string // Accepted image formats
defaultStamp?: string[] // Default stamp list
editor?: { // Stamp editor config
defaultBackgroundColor?: string
defaultBorderColor?: string
defaultBorderStyle?: 'none' | 'solid' | 'dashed'
defaultTextColor?: string
defaultFont?: {
label: string
value: string
}[]
}
}
}
Note:
signature.typevalues are'Draw' | 'Enter' | 'Upload'(capitalized), NOT'draw' | 'text' | 'image'.