Annotation Plugin

The Annotation Plugin provides a comprehensive framework for adding, editing, and managing annotations within a PDF document. It supports a wide range of common annotation types, including text markups (highlight, underline), free-hand drawings (ink), shapes (squares, circles), and more.

The plugin is built on an extensible “Tool” system, allowing you to define and customize different annotation behaviors and appearances.

Installation

This plugin has several dependencies that must be installed to handle user interactions and manage state. The history plugin is optional but recommended for undo/redo functionality.

npm install @embedpdf/plugin-annotation @embedpdf/plugin-interaction-manager @embedpdf/plugin-selection @embedpdf/plugin-history

Registration

Import AnnotationPluginPackage and its dependencies. It is crucial to register the dependencies before the annotation plugin itself.

import { createPluginRegistration } from '@embedpdf/core' import { EmbedPDF } from '@embedpdf/core/react' // ... other imports import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/react' import { SelectionPluginPackage } from '@embedpdf/plugin-selection/react' import { HistoryPluginPackage } from '@embedpdf/plugin-history/react' import { AnnotationPluginPackage } from '@embedpdf/plugin-annotation/react' const plugins = [   // ... other essential plugins   createPluginRegistration(LoaderPluginPackage, { /* ... */ }),   createPluginRegistration(RenderPluginPackage),   // Register dependencies first   createPluginRegistration(InteractionManagerPluginPackage),   createPluginRegistration(SelectionPluginPackage),   createPluginRegistration(HistoryPluginPackage),   // Register and configure the annotation plugin   createPluginRegistration(AnnotationPluginPackage, {     // Optional: Set the author name for created annotations     annotationAuthor: 'Jane Doe',   }), ]

Usage

The plugin works by combining a UI component to render the annotations with a hook to manage the annotation tools and state.

1. The <AnnotationLayer /> Component

This component is responsible for rendering all annotations and handling user interactions like creating, selecting, moving, and resizing. It must be placed inside the <Scroller />’s renderPage prop and wrapped by a <PagePointerProvider> to correctly process pointer events.

import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/react'; import { AnnotationLayer } from '@embedpdf/plugin-annotation/react'; // ... <Scroller   renderPage={({ width, height, pageIndex, scale, rotation }) => (     <PagePointerProvider {...{ width, height, pageIndex, scale, rotation }}>       <RenderLayer pageIndex={pageIndex} scale={scale} />       <SelectionLayer pageIndex={pageIndex} scale={scale} />       {/* Annotation Layer on top */} <AnnotationLayer pageIndex={pageIndex} scale={scale} pageWidth={width} pageHeight={height} rotation={rotation} />     </PagePointerProvider>   )} />

2. Building an Annotation Toolbar

The useAnnotationCapability hook provides all the necessary methods to control the plugin. You can build a toolbar that allows users to select an “active tool” (like a pen or highlighter) and perform actions like deleting a selected annotation.

import { useState, useEffect } from 'react'; import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react'; const AnnotationToolbar = () => {   const { provides: annotationApi } = useAnnotationCapability();   const [selected, setSelected] = useState(false);   useEffect(() => {     if (!annotationApi) return;     // Listen for selection changes to enable/disable the delete button     return annotationApi.onStateChange((state) => setSelected(!!state.selectedUid));   }, [annotationApi]);   const deleteSelected = () => {     const selection = annotationApi?.getSelectedAnnotation();     if (selection) {       annotationApi?.deleteAnnotation(selection.object.pageIndex, selection.object.id);     }   }   return (     <div>       <button onClick={() => annotationApi?.setActiveTool('highlight')}>Highlighter</button>       <button onClick={() => annotationApi?.setActiveTool('ink')}>Pen</button>       <button onClick={deleteSelected} disabled={!selected}>Delete</button>     </div>   ); };

3. Creating Custom Tools

You can extend the plugin’s functionality by adding your own tools. For example, you could create a custom image stamp. This is done by calling addTool on the plugin’s capability, often within the onInitialized callback of the <EmbedPDF> component.

<EmbedPDF   // ...   onInitialized={(registry) => {     const annotationApi = registry.getPlugin('annotation')?.provides();     annotationApi?.addTool({       id: 'stampApproved',       name: 'Approved Stamp',       interaction: { exclusive: false, cursor: 'copy' },       matchScore: () => 0, // Doesn't match existing annotations       defaults: {         type: PdfAnnotationSubtype.STAMP,         imageSrc: '/images/approved-stamp.png', // URL to your stamp image       },     });   }} >   {/* ... */} </EmbedPDF>

Listening to Annotation Events

For more advanced integrations, such as saving annotation data to a backend or synchronizing state with an external data store, you can subscribe to annotation lifecycle events using onAnnotationEvent.

This method allows you to react specifically to when annotations are created, updated, or deleted. Each event provides the annotation object and a committed flag, which indicates whether the change has been saved to the underlying PDF document in the engine.

import { useEffect } from 'react'; import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react'; const AnnotationLogger = () => {   const { provides: annotationApi } = useAnnotationCapability();   useEffect(() => {     if (!annotationApi) return;     const unsubscribe = annotationApi.onAnnotationEvent((event) => {       console.log(`Annotation event: ${event.type}`, { event });       // Example: Save to backend after a change is committed to the engine       if (event.type === 'create' && event.committed) {         // yourApi.saveAnnotation(event.annotation);       }     });     // Clean up the subscription when the component unmounts     return unsubscribe;   }, [annotationApi]);   return null; // This is a non-visual component };

Default Annotation Tools

The plugin comes with a set of pre-configured tools. You can activate any of these tools by passing its id to the setActiveTool method. You can also override their default properties by providing a modified AnnotationTool object with a matching id in the plugin configuration.

Tool NameidDescription
HighlighthighlightCreates text highlight annotations.
UnderlineunderlineCreates text underline annotations.
StrikeoutstrikeoutCreates text strikeout annotations.
SquigglysquigglyCreates squiggly text underline annotations.
PeninkFree-hand drawing tool.
Ink HighlighterinkHighlighterFree-hand highlighter with a multiply blend mode.
CirclecircleDraws ellipse annotations.
SquaresquareDraws rectangle annotations.
LinelineDraws straight line annotations.
ArrowlineArrowDraws a straight line with an arrowhead.
PolylinepolylineDraws multi-segment lines.
PolygonpolygonDraws closed, multi-sided shapes.
Free TextfreeTextAdds a text box annotation.
ImagestampAdds an image stamp. Opens a file picker by default.

Live Example

The example below includes a toolbar for selecting different annotation tools (highlight, ink pen, square) and a delete button that becomes active when an annotation is selected.

Loading PDF Engine...

API Reference

Configuration (AnnotationPluginConfig)

OptionTypeDescription
annotationAuthorstringSets the author name for all created annotations. Default: 'Guest'
autoCommitbooleanIf true, annotation changes are automatically saved to the engine. Default: true
toolsAnnotationTool[]An array of custom annotation tools to add or override default tools.
colorPresetsstring[]A list of hex color strings to be used in a color picker UI.
deactivateToolAfterCreatebooleanIf true, the active tool is deselected after an annotation is created. Default: false
selectAfterCreatebooleanIf true, a newly created annotation is automatically selected. Default: true

Component: <AnnotationLayer />

The primary component for rendering and interacting with annotations.

PropTypeDescription
pageIndexnumber(Required) The page index this layer corresponds to.
scalenumber(Required) The current zoom scale of the page.
rotationnumber(Required) The current rotation of the page.
pageWidthnumber(Required) The unscaled width of the PDF page.
pageHeightnumber(Required) The unscaled height of the PDF page.
selectionMenu(props) => JSX.ElementA render prop for a custom menu that appears when an annotation is selected.
resizeUI / vertexUIobjectObjects to customize the appearance of resize and vertex handles.
selectionOutlineColorstringThe color of the outline around a selected annotation. Default: '#007ACC'

Customizing Handles (resizeUI and vertexUI)

You can customize the visual appearance of the interactive handles for resizing and editing vertices. Both resizeUI and vertexUI accept an object with the following shape:

interface HandleUI {   /** Handle size in CSS pixels */   size?: number; // Default: 12   /** Default background color for the handle */   color?: string; // Default: '#007ACC'   /** Custom component to render for each handle */   component?: (props: HandleProps) => JSX.Element; }

The HandleProps passed to a custom component include positioning styles and event handlers, which must be applied to your custom element.

Hook: useAnnotationCapability()

Connects your components to the annotation plugin’s state and methods.

Returns

PropertyTypeDescription
providesAnnotationCapability | nullAn object with methods to control the plugin, or null if not ready.

AnnotationCapability Methods

A selection of key methods available on the provides object:

MethodDescription
setActiveTool(toolId)Activates an annotation tool (e.g., 'ink', 'highlight'). Pass null to deactivate.
getActiveTool()Returns the currently active AnnotationTool object, or null.
addTool(tool)Registers a new custom AnnotationTool.
createAnnotation(..)Programmatically creates a new annotation on a page.
updateAnnotation(..)Updates the properties of an existing annotation.
deleteAnnotation(..)Deletes an annotation from a page.
selectAnnotation(..)Programmatically selects an annotation.
getSelectedAnnotation()Returns the currently selected TrackedAnnotation object, or null.
importAnnotations(..)Imports an array of annotations into the viewer.
commit()Manually saves all pending annotation changes to the PDF document.
onStateChange(cb)Subscribes to any change in the annotation state.
onAnnotationEvent(cb)Subscribes to events like annotation creation, updates, or deletion.

The AnnotationEvent Object

The object passed to the onAnnotationEvent callback contains details about the lifecycle event. Its shape varies based on the type property.

Event TypePropertiesDescription
'create'annotation, pageIndex, ctx, committedFired when an annotation is created. ctx may contain extra data like ImageData for stamps. committed is true if the change has been saved to the engine.
'update'annotation, pageIndex, patch, committedFired when an annotation is moved, resized, or its properties change. annotation is the original object, and patch contains only the changed properties.
'delete'annotation, pageIndex, committedFired when an annotation is deleted.
'loaded'totalFired once when the initial set of annotations is loaded from the document. total is the number of annotations loaded.
Last updated on October 27, 2025

Need Help?

Join our community for support, discussions, and to contribute to EmbedPDF's development.