Annotations

The toolbar includes highlight, freehand, rectangle, ellipse, line, arrow, and text tools. Annotations are viewer overlays only - they never get written into the PDF when you download or print. Persist changes with onAnnotationChange.

Built-in tools

  • Highlight (color + opacity)
  • Freehand ink (color, thickness, dotted)
  • Shapes: rectangle, ellipse, line, arrow (fill, stroke, dotted)
  • Free text (font, size, align, bold/italic/underline, background)

Select, move, resize, erase

Annotation tools sit on the main toolbar. Clicking a tool (or an existing annotation) opens a Canva-style style modal with color swatches, opacity, fill, thickness, and size. Choose Draw on page to place annotations; a floating chip reopens settings anytime.

  • Drag the body to move
  • Resize with 8 handles (4 corners + 4 edges), or width/height sliders on the format bar for highlights and rectangles
  • Rectangles/ellipses support filled mode with separate fill color and fill opacity
  • Click the red (x) or press Delete / Backspace to erase
  • Double-click text annotations to re-edit content
  • Escape clears selection and exits the active tool

Download excludes annotations

Download and print always export the original PDF bytes. Overlay annotations stay in the viewer (and in your app state if you persist them). This keeps the source file clean even after heavy markup sessions.

Persist annotations

Listen for changes and store the array. Restore later with the ref API or by rehydrating stored payloads via parseAnnotations:

tsx
import {
  DocumentViewer,
  serializeAnnotations,
  parseAnnotations,
} from "lumipdf";

<DocumentViewer
  source={{ kind: "url", url: "/contract.pdf" }}
  onAnnotationChange={(annotations) => {
    localStorage.setItem(
      "contract-annotations",
      serializeAnnotations(annotations),
    );
  }}
/>

// Restore
const annotations = parseAnnotations(
  localStorage.getItem("contract-annotations"),
);
// ref.setAnnotations(annotations)

Imperative control

tsx
import { useRef } from "react";
import { DocumentViewer, type PdfViewerRef } from "lumipdf";

const ref = useRef<PdfViewerRef>(null);

// ref.current?.addAnnotation(...)
// ref.current?.deleteAnnotation(id)
// ref.current?.clearAnnotations()
// ref.current?.setActiveTool("highlight")
// ref.current?.download() // original PDF, no annotations