Complete reference for all components, functions, and types in react-pdf-highlighter-plus.
Extracts sentence-level text from a PDF.js document and returns sentence metadata with optional highlight-compatible positions.
import { extractSentences } from "react-pdf-highlighter-plus";
const sentences = await extractSentences(pdfDocument);| Option | Type | Default | Description |
|---|---|---|---|
pages |
"all" | number[] |
"all" |
Pages to extract from |
includePositions |
boolean |
true |
Include ScaledPosition values for rendering highlights |
normalize |
boolean |
true |
Normalize whitespace, line-break hyphenation, and citation spacing |
locale |
string |
"en" |
Locale used by Intl.Segmenter |
idPrefix |
string |
"" |
Prefix applied to generated sentence ids |
readingOrder |
"auto" | "document" | "position" |
"auto" |
Controls whether extraction uses detected layout order or raw PDF order |
columnDetection |
"auto" | "none" |
"auto" |
Detect body columns and read each column top-to-bottom |
const sentences = await extractSentences(pdfDocument, {
pages: [1, 2],
idPrefix: "ai-",
});
console.log(sentences[0]);
// {
// id: "ai-p1-s0",
// text: "JavaScript enables rich interactions.",
// rawText: "JavaScript enables rich interactions.",
// pageNumber: 1,
// indexInPage: 0,
// globalIndex: 0,
// position: { boundingRect, rects },
// source: { startOffset, endOffset, textItemIndexes }
// }Returns lower-level PDF.js text items with page-relative rectangles. Use this when you need custom text grouping instead of sentence extraction.
import { extractPageTextItems } from "react-pdf-highlighter-plus";
const pages = await extractPageTextItems(pdfDocument, { pages: [1] });Extracts layout-aware text units before sentence splitting. This is useful for research papers where titles, author blocks, section headings, footnotes, reference links, and two-column body layouts need local layout handling before sentence extraction.
import { extractTextUnits } from "react-pdf-highlighter-plus";
const units = await extractTextUnits(pdfDocument, { pages: [1] });Each unit has a type:
"paragraph" | "title" | "heading" | "author" | "affiliation" | "footnote" | "reference" | "unknown"extractSentences() uses this layer and splits only "paragraph" units by default. To include other unit types:
const sentences = await extractSentences(pdfDocument, {
includeTextUnitTypes: ["paragraph", "title", "heading"],
});Two-column academic pages are detected automatically. Extracted units and sentences include columnIndex when a page has detected body columns. Use columnDetection: "none" to disable this and keep single-column grouping.
The extractor also repairs common PDF text artifacts: per-glyph output such as T h i n k, jammed word boundaries in common academic prose, spaced hyphen compounds, citation spacing, and common ligatures such as fi.
The example app JSON export groups results by page:
{
"pageSelection": "all",
"pages": [
{
"pageNumber": 1,
"columns": [],
"textUnits": [],
"sentences": []
}
]
}Converts a positioned sentence into a standard text highlight.
import {
extractSentences,
sentenceToHighlight,
} from "react-pdf-highlighter-plus";
const sentences = await extractSentences(pdfDocument);
const highlights = sentences
.filter((sentence) => sentence.position)
.map((sentence) => sentenceToHighlight(sentence));For AI workflows, keep model calls in your app layer:
const sentences = await extractSentences(pdfDocument);
const aiInput = sentences.map(({ id, text }) => ({ id, text }));
const aiResults = await analyzeSentences(aiInput);
const highlights = sentences
.filter((sentence) => aiResults[sentence.id]?.important)
.map((sentence) => sentenceToHighlight(sentence));Locate a piece of text in the PDF and return its precise position (the rects of the exact phrase, per line — not a whole sentence). Use it to turn an external quote / AI citation into a highlight you can render or scroll to. Matching ignores whitespace and line-wraps, with a bounded fuzzy fallback.
import { getTextPosition } from "react-pdf-highlighter-plus";
const match = await getTextPosition(pdfDocument, "the quote to locate");
// match: { position: ScaledPosition; pageNumber: number;
// matchedText: string; confidence: "exact" | "fuzzy" } | null
if (match) {
const highlight = {
id: "cite-1",
type: "text",
content: { text: match.matchedText },
position: match.position,
};
setHighlights((prev) => [highlight, ...prev]);
utils.scrollToHighlight(highlight); // smooth scroll + flash
}Options: getTextPosition(pdfDocument, query, { pages?: "all" | number[]; fuzzy?: boolean })
Renders selected PDF text as highlight rectangles.
import { TextHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | Text highlight data |
isScrolledTo |
boolean |
false |
Whether highlight was auto-scrolled to |
highlightColor |
string |
Yellow | Highlight color |
highlightStyle |
"highlight" | "underline" | "strikethrough" |
"highlight" |
Text highlight style |
copyText |
string |
- | Text copied by the toolbar copy button |
onStyleChange |
(style: TextHighlightStyle) => void |
- | Called when style changes |
onDelete |
() => void |
- | Called when delete is clicked |
Geometry renders in .PdfHighlighter__highlight-layer; toolbar, style panel, and copy button render in .PdfHighlighter__config-layer.
Renders a draggable, resizable rectangular area annotation.
import { AreaHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | Area highlight data |
onChange |
(rect: LTWHP) => void |
- | Called when position/size changes |
isScrolledTo |
boolean |
false |
Whether highlight was auto-scrolled to |
bounds |
string | Element |
- | Bounds for dragging/resizing |
highlightColor |
string |
Yellow | Area color |
copyText |
string |
- | Text copied by the toolbar copy button |
onStyleChange |
(style: AreaHighlightStyle) => void |
- | Called when style changes |
onDelete |
() => void |
- | Called when delete is clicked |
When copyText is not provided, the copy button extracts intersecting text from the PDF.js text layer.
A draggable, editable text annotation component.
import { FreetextHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | The highlight data to render |
onChange |
(rect: LTWHP) => void |
- | Called when position changes |
onTextChange |
(text: string) => void |
- | Called when text content changes |
onStyleChange |
(style: FreetextStyle) => void |
- | Called when style changes via panel |
isScrolledTo |
boolean |
false |
Whether highlight was auto-scrolled to |
bounds |
string | Element |
- | Bounds for dragging (react-rnd) |
onContextMenu |
(event: MouseEvent) => void |
- | Right-click handler |
onEditStart |
() => void |
- | Called when editing begins |
onEditEnd |
() => void |
- | Called when editing ends |
style |
CSSProperties |
- | Custom container styling |
color |
string |
"#333333" |
Text color |
backgroundColor |
string |
"#ffffc8" |
Background color |
fontFamily |
string |
"inherit" |
Font family |
fontSize |
string |
"14px" |
Font size |
dragIcon |
ReactNode |
6-dot grid | Custom drag handle icon |
editIcon |
ReactNode |
Pencil | Custom edit button icon |
styleIcon |
ReactNode |
Palette | Custom style button icon |
backgroundColorPresets |
string[] |
See below | Background color presets |
textColorPresets |
string[] |
See below | Text color presets |
compact |
boolean |
false |
Render the note as a compact marker until opened |
compactSize |
number |
32 |
Compact marker size in pixels |
compactIcon |
ReactNode |
Note icon | Custom compact marker icon |
Default Color Presets:
- Background:
["#ffffc8", "#ffcdd2", "#c8e6c9", "#bbdefb", "#e1bee7"] - Text:
["#333333", "#d32f2f", "#1976d2", "#388e3c", "#7b1fa2"]
<FreetextHighlight
highlight={highlight}
isScrolledTo={isScrolledTo}
onChange={(rect) => updatePosition(highlight.id, rect)}
onTextChange={(text) => updateText(highlight.id, text)}
onStyleChange={(style) => updateStyle(highlight.id, style)}
color={highlight.color}
backgroundColor={highlight.backgroundColor}
fontSize={highlight.fontSize}
/>A draggable, resizable image annotation component.
import { ImageHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | The highlight data (must have content.image) |
onChange |
(rect: LTWHP) => void |
- | Called when position/size changes |
isScrolledTo |
boolean |
false |
Whether highlight was auto-scrolled to |
bounds |
string | Element |
- | Bounds for dragging |
onContextMenu |
(event: MouseEvent) => void |
- | Right-click handler |
onEditStart |
() => void |
- | Called when drag/resize begins |
onEditEnd |
() => void |
- | Called when drag/resize ends |
style |
CSSProperties |
- | Custom container styling |
dragIcon |
ReactNode |
6-dot grid | Custom drag handle icon |
onStyleChange |
(image: string, strokes: DrawingStroke[]) => void |
- | Called after editing drawing color/width |
onDelete |
() => void |
- | Called when delete is clicked |
Drawing geometry renders in .PdfHighlighter__highlight-layer; toolbar/style controls render in .PdfHighlighter__config-layer.
<ImageHighlight
highlight={highlight}
isScrolledTo={isScrolledTo}
bounds={highlightBindings.textLayer}
onChange={(rect) => updatePosition(highlight.id, rect)}
onEditStart={() => toggleEditInProgress(true)}
onEditEnd={() => toggleEditInProgress(false)}
/>A draggable, resizable freehand drawing component.
import { DrawingHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | The highlight data (must have content.image) |
onChange |
(rect: LTWHP) => void |
- | Called when position/size changes |
isScrolledTo |
boolean |
false |
Whether highlight was auto-scrolled to |
bounds |
string | Element |
- | Bounds for dragging |
onContextMenu |
(event: MouseEvent) => void |
- | Right-click handler |
onEditStart |
() => void |
- | Called when drag/resize begins |
onEditEnd |
() => void |
- | Called when drag/resize ends |
style |
CSSProperties |
- | Custom container styling |
dragIcon |
ReactNode |
6-dot grid | Custom drag handle icon |
<DrawingHighlight
highlight={highlight}
isScrolledTo={isScrolledTo}
bounds={highlightBindings.textLayer}
onChange={(rect) => updatePosition(highlight.id, rect)}
onEditStart={() => toggleEditInProgress(true)}
onEditEnd={() => toggleEditInProgress(false)}
/>A draggable, resizable rectangle, circle, or arrow annotation.
import { ShapeHighlight } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
highlight |
ViewportHighlight |
Required | The highlight data |
shapeType |
"rectangle" | "circle" | "arrow" |
"rectangle" |
Shape type to render |
strokeColor |
string |
"#000000" |
Stroke color |
strokeWidth |
number |
2 |
Stroke width |
startPoint |
{ x: number; y: number } |
- | Arrow start point as a ratio of bounds |
endPoint |
{ x: number; y: number } |
- | Arrow end point as a ratio of bounds |
onChange |
(rect: LTWHP) => void |
- | Called when position/size changes |
onStyleChange |
(style: ShapeStyle) => void |
- | Called when stroke style changes |
onDelete |
() => void |
- | Called when delete is clicked |
Shape geometry renders in .PdfHighlighter__highlight-layer; toolbar/style controls render in .PdfHighlighter__config-layer.
A modal component for drawing signatures.
import { SignaturePad } from "react-pdf-highlighter-plus";| Prop | Type | Default | Description |
|---|---|---|---|
isOpen |
boolean |
Required | Whether the modal is visible |
onComplete |
(dataUrl: string) => void |
Required | Called with PNG data URL when done |
onClose |
() => void |
Required | Called when modal is closed |
width |
number |
400 |
Canvas width in pixels |
height |
number |
200 |
Canvas height in pixels |
<SignaturePad
isOpen={isOpen}
onComplete={(dataUrl) => {
setPendingImage(dataUrl);
setImageMode(true);
setIsOpen(false);
}}
onClose={() => setIsOpen(false)}
width={400}
height={200}
/>Export a PDF with annotations embedded.
import { exportPdf } from "react-pdf-highlighter-plus";async function exportPdf(
pdfSource: string | Uint8Array | ArrayBuffer,
highlights: ExportableHighlight[],
options?: ExportPdfOptions
): Promise<Uint8Array>| Parameter | Type | Description |
|---|---|---|
pdfSource |
string | Uint8Array | ArrayBuffer |
PDF as URL, bytes, or buffer |
highlights |
ExportableHighlight[] |
Highlights to embed |
options |
ExportPdfOptions |
Export configuration |
| Option | Type | Default | Description |
|---|---|---|---|
textHighlightColor |
string |
"rgba(255, 226, 143, 0.5)" |
Default text highlight color |
areaHighlightColor |
string |
"rgba(255, 226, 143, 0.5)" |
Default area highlight color |
defaultFreetextColor |
string |
"#333333" |
Default freetext text color |
defaultFreetextBgColor |
string |
"#ffffc8" |
Default freetext background |
defaultFreetextFontSize |
number |
14 |
Default freetext font size |
onProgress |
(current, total) => void |
- | Progress callback |
const pdfBytes = await exportPdf(pdfUrl, highlights, {
textHighlightColor: "rgba(255, 226, 143, 0.5)",
onProgress: (current, total) => console.log(`${current}/${total}`),
});
// Download
const blob = new Blob([pdfBytes], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "annotated.pdf";
a.click();
URL.revokeObjectURL(url);| Prop | Type | Default | Description |
|---|---|---|---|
theme |
PdfHighlighterTheme |
Light theme | Theme configuration for light/dark mode |
interface PdfHighlighterTheme {
/** Theme mode */
mode?: "light" | "dark";
/** Background color of the viewer container */
containerBackgroundColor?: string;
/** Scrollbar thumb color */
scrollbarThumbColor?: string;
/** Scrollbar track color */
scrollbarTrackColor?: string;
/**
* Dark-mode recolor palette (hue-preserving OKLab). White paper maps to
* `background`, black text / line-art to `foreground`. Only used in dark mode.
* @default { background: "#141210", foreground: "#eae6e0" }
*/
darkModeColors?: { background: string; foreground: string };
/** @deprecated No longer used — dark mode no longer uses a CSS invert filter. */
darkModeInvertIntensity?: number;
}Light Theme (default):
{
mode: "light",
containerBackgroundColor: "#e5e5e5",
scrollbarThumbColor: "#9f9f9f",
scrollbarTrackColor: "#d1d1d1",
}Dark Theme:
{
mode: "dark",
darkModeColors: { background: "#141210", foreground: "#eae6e0" },
containerBackgroundColor: "#3a3a3a", // Lighter than the page for contrast
scrollbarThumbColor: "#6b6b6b",
scrollbarTrackColor: "#2c2c2c",
}// Simple dark mode
<PdfHighlighter
pdfDocument={pdfDocument}
theme={{ mode: "dark" }}
/>
// Custom dark palette
<PdfHighlighter
pdfDocument={pdfDocument}
theme={{
mode: "dark",
darkModeColors: { background: "#0d1117", foreground: "#e6edf3" },
containerBackgroundColor: "#2a2a2a",
}}
/>
// Dynamic theme toggle
const [darkMode, setDarkMode] = useState(false);
<PdfHighlighter
pdfDocument={pdfDocument}
theme={{ mode: darkMode ? "dark" : "light" }}
/>| Prop | Type | Description |
|---|---|---|
enableFreetextCreation |
(event: MouseEvent) => boolean |
Returns true when freetext mode is active |
onFreetextClick |
(position: ScaledPosition) => void |
Called when user clicks to create freetext |
| Prop | Type | Description |
|---|---|---|
enableImageCreation |
(event: MouseEvent) => boolean |
Returns true when image mode is active |
onImageClick |
(position: ScaledPosition) => void |
Called when user clicks to place image |
| Prop | Type | Description |
|---|---|---|
enableDrawingMode |
boolean |
Whether drawing mode is active |
onDrawingComplete |
(dataUrl: string, position: ScaledPosition, strokes: DrawingStroke[]) => void |
Called when drawing is finished |
drawingStrokeColor |
string |
Drawing stroke color |
drawingStrokeWidth |
number |
Drawing stroke width |
| Prop | Type | Description |
|---|---|---|
enableShapeMode |
"rectangle" | "circle" | "arrow" | null |
Active shape creation mode |
onShapeComplete |
(position: ScaledPosition, shape: ShapeData) => void |
Called when shape creation is finished |
shapeStrokeColor |
string |
Shape stroke color |
shapeStrokeWidth |
number |
Shape stroke width |
PdfHighlighterUtils exposes search(query, options), findNext(), findPrevious(), and clearSearch() using PDF.js PDFFindController.
type HighlightType = "text" | "area" | "freetext" | "image" | "drawing" | "shape";interface Highlight {
id: string;
type?: HighlightType;
position: ScaledPosition;
content?: {
text?: string;
image?: string;
strokes?: DrawingStroke[];
shape?: ShapeData;
};
}interface ExportableHighlight {
id: string;
type?: HighlightType;
content?: {
text?: string;
image?: string;
};
position: ScaledPosition;
highlightColor?: string; // For text/area
color?: string; // For freetext
backgroundColor?: string; // For freetext
fontSize?: string; // For freetext
fontFamily?: string; // For freetext
}interface FreetextStyle {
color?: string;
backgroundColor?: string;
fontFamily?: string;
fontSize?: string;
}interface LTWHP {
left: number;
top: number;
width: number;
height: number;
pageNumber: number;
}interface ScaledPosition {
boundingRect: Scaled;
rects: Array<Scaled>;
usePdfCoordinates?: boolean;
}interface Scaled {
x1: number;
y1: number;
x2: number;
y2: number;
width: number;
height: number;
pageNumber: number;
}type ViewportHighlight<T extends Highlight = Highlight> = Omit<T, "position"> & {
position: ViewportPosition;
};interface ViewportPosition {
boundingRect: LTWHP;
rects: Array<LTWHP>;
}.FreetextHighlight { }
.FreetextHighlight__container { }
.FreetextHighlight__toolbar { }
.FreetextHighlight__drag-handle { }
.FreetextHighlight__edit-button { }
.FreetextHighlight__style-button { }
.FreetextHighlight__style-panel { }
.FreetextHighlight__text { }
.FreetextHighlight__input { }
.FreetextHighlight--scrolledTo { }
.FreetextHighlight--editing { }.ImageHighlight { }
.ImageHighlight__container { }
.ImageHighlight__toolbar { }
.ImageHighlight__drag-handle { }
.ImageHighlight__content { }
.ImageHighlight__image { }
.ImageHighlight--scrolledTo { }.DrawingHighlight { }
.DrawingHighlight__container { }
.DrawingHighlight__toolbar { }
.DrawingHighlight__drag-handle { }
.DrawingHighlight__content { }
.DrawingHighlight__image { }
.DrawingHighlight--scrolledTo { }.SignaturePad__overlay { }
.SignaturePad__modal { }
.SignaturePad__title { }
.SignaturePad__canvas { }
.SignaturePad__buttons { }
.SignaturePad__button { }
.SignaturePad__button--clear { }
.SignaturePad__button--cancel { }
.SignaturePad__button--done { }