To create a PDF viewer in React.js using PDF.js, especially with support for layers (like text, annotations, and images), you need a good understanding of how PDF.js renders documents. Here’s a full guide:
Understanding PDF.js Layers
PDF.js renders PDFs using HTML5. It separates rendering into three main layers:
Layer | Purpose | Output |
canvas | Renders PDF content (text, images, drawings) | Bitmap |
textLayer | Overlays selectable text (for copy/paste or search) | HTML |
annotationLayer | Adds interactive elements (links, form fields, etc.) | HTML |
XfaLayer (optional) | For newer interactive forms (XFA-based) | HTML |
Best Way to Build a PDF Viewer in React
Option 1: Use pdfjs-dist
+ custom React component (for flexibility)
Option 2: Use a wrapper library like react-pdf
for ease
Option 1: Full Control Using pdfjs-dist
Install:
npm install pdfjs-dist
Set up:
import { useEffect, useRef } from 'react';
import { getDocument } from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
import * as pdfjsLib from 'pdfjs-dist/build/pdf';
import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
function PDFViewer({ pdfUrl }) {
const containerRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
const eventBus = new pdfjsViewer.EventBus();
const pdfLinkService = new pdfjsViewer.PDFLinkService({ eventBus });
const pdfViewer = new pdfjsViewer.PDFViewer({
container,
eventBus,
linkService: pdfLinkService,
textLayerMode: 2, // enables selectable text
annotationMode: 2, // enables annotations
});
pdfLinkService.setViewer(pdfViewer);
getDocument(pdfUrl).promise.then(pdfDoc => {
pdfViewer.setDocument(pdfDoc);
pdfLinkService.setDocument(pdfDoc);
});
}, [pdfUrl]);
return (
<div className="pdfViewer" ref={containerRef} style={{ height: '100vh', overflow: 'scroll' }} />
);
}
This supports textLayer, annotationLayer, and has full control.
Option 2: Easier with react-pdf
(but less control over layers)
Install:
npm install @react-pdf-viewer/core @react-pdf-viewer/default-layout
Use:
import { Worker, Viewer } from '@react-pdf-viewer/core';
import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout';
import '@react-pdf-viewer/core/lib/styles/index.css';
import '@react-pdf-viewer/default-layout/lib/styles/index.css';
const MyPdfViewer = ({ fileUrl }) => {
const defaultLayoutPluginInstance = defaultLayoutPlugin();
return (
<Worker workerUrl={`https://unpkg.com/pdfjs-dist@3.4.120/build/pdf.worker.min.js`}>
<div style={{ height: '750px' }}>
<Viewer fileUrl={fileUrl} plugins={[defaultLayoutPluginInstance]} />
</div>
</Worker>
);
};
This is easier to integrate but hides some PDF.js layer-level control under the hood.
Tips
- To enable copy/select, ensure
textLayerMode: 2
is used (or equivalent in viewer).
- For annotations (e.g., links, form fields), make sure
annotationMode: 2
.
- Add styles from
pdfjs-dist/web/pdf_viewer.css
for correct rendering.
- Use a PDF worker for performance (PDF rendering off main thread).
Recommendation
If you want | Use |
Full control (text, links, annotations, custom layout) | pdfjs-dist with custom setup |
Simplicity & decent features | react-pdf-viewer or @react-pdf-viewer |
Heavy customization (e.g., drawing, signature, comments) | Go with pdfjs-dist + custom UI |