Package Exports
- @nicklasastorian/react-pdf-annotator
- @nicklasastorian/react-pdf-annotator/dist/src/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@nicklasastorian/react-pdf-annotator) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
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);
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