@nicklasastorian/react-pdf-annotator
v1.2.7
Published
Extended Set of React components for PDF annotation
Maintainers
Readme
react-pdf-highlighter
Set of React components for PDF annotation.
Note: This is a fork of agentcooper/react-pdf-highlighter with significant UX improvements.
Features
- Built on top of PDF.js
- Text and image highlights
- Popover text for highlights
- Scroll to highlights
Fork Improvements
This fork includes the following enhancements:
Highlight Interaction
- Hover-based action bar: Shows on hover with 200ms delay (prevents flickering), instead of right-click
- Smooth hover transitions: 150ms grace period allows mouse to move from highlight to action bar
- Click protection: Clicking action bar buttons no longer dismisses the popup
Selection Flow
- Mouseup-based selection: Toolbar appears only after releasing mouse, not while dragging
- Morphing toolbar: After saving, toolbar transitions from "save mode" to "edit mode"
- No double-yellow: Browser selection is cleared on save to prevent color stacking
Positioning
- Centered popups: Action bars are horizontally centered above highlights
- Smart clamping: Popups stay within page bounds while maintaining optimal position
Visual
- Better highlight color: More vibrant yellow (
rgba(255, 212, 0, 0.4)) - Hover feedback: Highlights brighten slightly on hover
New Props
| Prop | Description |
|------|-------------|
| renderPopup | Simpler alternative to highlightTransform for hover action bars |
| onTextLayerReady | Callback when a page's text layer is rendered |
| onDocumentReady | Callback when document is ready |
| forceRenderOnLoad | Force re-render highlights when all pages loaded |
| onHighlightHover | Callback when a highlight is hovered |
| onHighlightBlur | Callback when a highlight loses hover |
| disallowOverlappingHighlights | Prevent creating overlapping text highlights on the same line |
| onOverlap | Callback fired when a selection overlaps an existing highlight |
Install
npm install react-pdf-highlighterImporting CSS
import "react-pdf-highlighter/dist/style.css";Example
npm install
npm startThe example demonstrates:
- Slide-over panel pattern
- Zoom controls (Ctrl+scroll or buttons)
- Morphing toolbar (save → edit)
- Comment dialog modal
- In-memory highlight persistence
Basic Usage
import { PdfHighlighter, PdfLoader } from "react-pdf-highlighter";
import "react-pdf-highlighter/dist/style.css";
<PdfLoader url={pdfUrl} beforeLoad={<Spinner />}>
{(pdfDocument) => (
<PdfHighlighter
pdfDocument={pdfDocument}
enableAreaSelection={(event) => event.altKey}
onScrollChange={() => {}}
scrollRef={(scrollTo) => { /* store scrollTo */ }}
onSelectionFinished={(position, content, hideTipAndSelection, transformSelection) => (
<MySelectionToolbar
onSave={() => {
saveHighlight({ position, content });
hideTipAndSelection();
}}
/>
)}
renderPopup={(highlight) => (
<MyActionBar highlight={highlight} />
)}
highlights={highlights}
/>
)}
</PdfLoader>useHighlights Helper
useHighlights is an optional convenience hook for managing highlight state while keeping your data source external (React Query, Redux, etc.).
import { useState } from "react";
import { useHighlights } from "react-pdf-highlighter";
const [highlights, setHighlights] = useState<IHighlight[]>([]);
const {
addHighlight,
updateHighlight,
deleteHighlight,
saveComment,
} = useHighlights({
highlights,
setHighlights,
});This pattern lets you merge/split your own persistence models in setHighlights and rehydrate via async fetches.
useZoom Helper
useZoom provides a small controller for zooming the PDF viewer (buttons + ctrl/cmd + wheel).
import { useRef } from "react";
import { useZoom } from "react-pdf-highlighter";
const containerRef = useRef<HTMLDivElement | null>(null);
const { pdfScaleValue, zoomLabel, zoomIn, zoomOut, fitWidth } = useZoom(
containerRef,
true,
);How It Works (Library)
High-level flow
PdfLoaderloads aPDFDocumentProxyusing PDF.js and hands it to your render prop.PdfHighlighterowns a PDF.jsPDFViewer, attaches selection/scroll listeners, and renders highlights into a per-page overlay layer.- Text selections call
onSelectionFinishedwith aScaledPositionand text content; area selections are handled byMouseSelectionand yield image content. - Highlights are rendered via
highlightTransform(custom) or the default transform, which choosesHighlightfor text orAreaHighlightfor images and can show a popup viarenderPopup.
Data model and coordinates
ScaledPositionis stored in PDF/viewport-independent units (optionallyusePdfCoordinates).Positionis viewport pixels used for rendering.scaledToViewportandviewportToScaledlive insrc/lib/coordinates.ts.IHighlightis the base interface for highlight objects;HighlightHooksexposesonCreate,onUpdate, andonDelete.
Project layout (library)
src/index.tsis the public entry point; it re-exports components, types, and the global CSS.src/components/containsPdfHighlighter,PdfLoader, and the overlay UI (Highlight,AreaHighlight,TipContainer).src/lib/contains PDF.js DOM helpers, coordinate conversions, and geometry utilities.src/style/holds CSS modules plusindex.csswhich imports the PDF.js viewer styles.
Build outputs
- Vite builds the package and emits JS + CSS into
dist/. - Types are emitted via
vite-plugin-dtsand exposed atdist/index.d.ts. - The published package exposes
dist/style.cssfor styling.
API Reference
See ./example/src/App.tsx for a complete implementation example.
API Improvement Ideas (Future)
These are optional, backward-compatible ideas that could simplify usage and improve extensibility:
- Decouple UI from selection: make
onSelectionFinishedreturn data/payload, with an optionalrenderSelectionfor UI. - Consolidate highlight types: collapse
HighlightPayload/HighlightBase/IHighlightinto a clearer, generic model (typedmeta). - Unified PDF.js options: accept a single
pdfjsOptionsobject (worker/cMap/viewer) to reduce prop surface area. - Public coordinate helpers: export
scaledToViewport/viewportToScaledfrom the public API for consumers.
License
MIT
