Annotations
The PDFViewer includes a powerful annotation system that supports highlights, ink drawings, shapes, and more. While the built-in toolbar provides a UI for these tools, you can also control them programmatically.
Configuration
You can configure annotation defaults like the author name using the annotation config object.
<PDFViewer
config={{
annotation: {
annotationAuthor: 'John Doe',
}
}}
/>Programmatic Control
You can control the active tool and manage annotations programmatically.
Setting the Active Tool
Use setActiveTool to switch between tools. Pass null to switch back to the default selection cursor.
import { AnnotationPlugin } from '@embedpdf/react-pdf-viewer';
const registry = await viewerRef.current?.registry;
const annotationPlugin = registry.getPlugin<AnnotationPlugin>('annotation')?.provides();
// Activate Highlighter
annotationPlugin?.setActiveTool('highlight');
// Activate Pen (Ink)
annotationPlugin?.setActiveTool('ink');
// Deactivate tool (switch to selection mode)
annotationPlugin?.setActiveTool(null);Common Tool IDs
| Tool | ID | Description |
|---|---|---|
| Highlight | 'highlight' | Text highlighter |
| Underline | 'underline' | Text underline |
| Ink (Pen) | 'ink' | Freehand drawing |
| Rectangle | 'square' | Square/Rectangle shape |
| Circle | 'circle' | Circle/Ellipse shape |
| Text | 'freeText' | Free text box |
| Note | 'text' | Sticky note |
Listening for Changes
You can listen for annotation events (creation, deletion, updates) to sync with a backend or update your UI.
const registry = await viewerRef.current?.registry;
const annotationPlugin = registry.getPlugin('annotation').provides();
// Listen for creation, updates, and deletion
annotationPlugin.onAnnotationEvent((event) => {
const { type, annotation, pageIndex } = event;
if (type === 'create') {
console.log('Created annotation:', annotation.id);
// e.g. save to backend
} else if (type === 'delete') {
console.log('Deleted annotation:', annotation.id);
}
});
// Listen for tool changes
annotationPlugin.onActiveToolChange(({ tool }) => {
console.log('Active tool:', tool ? tool.name : 'Selection');
});Importing and Exporting Annotations
You can export all annotations from a document and re-import them later. This is useful for saving annotation state to a backend or transferring annotations between sessions.
Exporting Annotations
exportAnnotations() returns a Task that resolves to an array of AnnotationTransferItem objects. Each item contains the annotation object and, for stamps, a ctx with the appearance data as an ArrayBuffer.
import { AnnotationPlugin, type AnnotationTransferItem } from '@embedpdf/react-pdf-viewer';
const registry = await viewerRef.current?.registry;
const api = registry?.getPlugin<AnnotationPlugin>('annotation')?.provides();
api?.exportAnnotations().wait(
(items: AnnotationTransferItem[]) => {
// items can be serialized and stored
console.log(`Exported ${items.length} annotations`);
},
(error) => console.error('Export failed', error),
);Importing Annotations
importAnnotations() accepts the same AnnotationTransferItem[] format returned by exportAnnotations(), making the round-trip seamless.
api?.importAnnotations(savedItems);For stamps, the ctx.data field accepts a raw ArrayBuffer containing PNG, JPEG, or PDF data — the engine detects the format automatically via magic bytes.
const pngBuffer = await fetch('/my-stamp.png').then(r => r.arrayBuffer());
api?.importAnnotations([
{
annotation: {
type: PdfAnnotationSubtype.STAMP,
rect: { origin: { x: 100, y: 200 }, size: { width: 50, height: 50 } },
// ... other annotation properties
},
ctx: { data: pngBuffer },
},
]);Adding Custom Stamp Tools
You can register custom stamp tools that place images with a single click. Access the annotation API after the viewer initializes:
import {
AnnotationPlugin,
type AnnotationTool,
PdfAnnotationSubtype,
type PdfStampAnnoObject,
} from '@embedpdf/react-pdf-viewer';
const registry = await viewerRef.current?.registry;
const api = registry?.getPlugin<AnnotationPlugin>('annotation')?.provides();
api?.addTool<AnnotationTool<PdfStampAnnoObject>>({
id: 'stampCheckmark',
name: 'Checkmark',
interaction: { exclusive: true, cursor: 'crosshair' },
matchScore: () => 0,
defaults: {
type: PdfAnnotationSubtype.STAMP,
imageSrc: '/circle-checkmark.png',
imageSize: { width: 30, height: 30 },
},
behavior: {
showGhost: true,
deactivateToolAfterCreate: true,
selectAfterCreate: true,
},
});Need Help?
Join our community for support, discussions, and to contribute to EmbedPDF's development.