React wrapper for pdfjs with multiple file support
npm install react-pdfjs-multiReact wrapper for pdfjs with multiple file support.
You can find a working example [here][2].
This library uses [pdf.js][1].
npm i react-pdfjs-multi
#### Minimal example
If you only need the defaults, you can render the component with just the pdfs array and the bundled CSS.
``tsx
import React from 'react';
import { PdfMultiViewer } from 'react-pdfjs-multi';
import workerSrc from 'react-pdfjs-multi/dist/pdf.worker.min.mjs?url';
import 'react-pdfjs-multi/dist/react-pdfjs-multi.css';
const MinimalMultiViewer = () => (
);
export default MinimalMultiViewer;
`
#### MultiViewer customization
This example picks a non-default document via initialLoadIndex, and the icons prop overrides the visual assets via CSS variables.
`tsx
import React from 'react';
import { PdfMultiViewer, type IconConfig } from 'react-pdfjs-multi';
import workerSrc from 'react-pdfjs-multi/dist/pdf.worker.min.mjs?url';
import 'react-pdfjs-multi/dist/react-pdfjs-multi.css';
const pdfs = ['/pdfs/sample-a.pdf', '/pdfs/sample-b.pdf'];
const icons: IconConfig = {
zoomIn: '/icons/zoom-in.svg',
zoomOut: '/icons/zoom-out.svg',
rotateLeft: '/icons/rotate-left.svg',
rotateRight: '/icons/rotate-right.svg',
download: '/icons/download.svg',
toggleList: '/icons/toggle-list.svg',
selectArrow: '/icons/select-arrow.svg',
texture: 'none',
};
const MultiViewerExample = () => (
workerSrc={workerSrc}
icons={icons}
rendererIcons={icons}
initialLoadIndex={1}
i18nData={{
download: 'Download',
scaleDown: 'Scale down',
scaleUp: 'Scale up',
}}
/>
);
export default MultiViewerExample;
`
#### PdfRenderer integration
When building your own viewer, assign the worker once and listen to the pdfChangeHook if you need to cache zoom/rotation/scroll positions manually.
`tsx
import React, { FC, useEffect, useState } from 'react';
import {
PdfRenderer,
PdfjsLib,
type PDFDocumentProxy,
type RendererDocumentPosition,
} from 'react-pdfjs-multi';
import workerSrc from 'react-pdfjs-multi/dist/pdf.worker.min.mjs?url';
PdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc;
const RendererExample: FC = () => {
const [pdfDoc, setPdfDoc] = useState
const logPosition = (index: string, position: RendererDocumentPosition) => {
console.log('document position', index, position);
};
useEffect(() => {
const load = async () => {
const doc = await PdfjsLib.getDocument({
url: '/pdfs/sample-a.pdf',
}).promise;
setPdfDoc(doc);
};
load();
}, []);
if (!pdfDoc) return null;
return
};
export default RendererExample;
`
A minimal workspace example lives at apps/example.
- apps/example/src/examples/DefaultExample.tsxapps/example/src/examples/ContrastExample.tsx
- + ContrastExample.css
`bash`
pnpm install
pnpm start
pnpm dev:example
This library ships with optional default styles in
react-pdfjs-multi/dist/react-pdfjs-multi.css. You can:
- Use the defaults as-is.
- Skip the CSS import and provide your own styles.
- Override the defaults via CSS variables.
Example override:
`css`
.pdfjs-multi {
--pdfjs-multi-bg: #f7f7f7;
--pdfjs-multi-text: #1f2933;
--pdfjs-multi-surface: #ffffff;
--pdfjs-multi-texture: none;
}
#### Theming recipe (example)
Here is a compact “high-contrast” pattern similar to the example app. It uses
icon overrides, CSS variables, and a scoped theme class:
`tsx
import React from 'react';
import type { IconConfig } from 'react-pdfjs-multi';
import { PdfMultiViewer } from 'react-pdfjs-multi';
const icons: IconConfig = {
zoomIn: '/icons/zoom-in.svg',
zoomOut: '/icons/zoom-out.svg',
rotateLeft: '/icons/rotate-left.svg',
rotateRight: '/icons/rotate-right.svg',
download: '/icons/download.svg',
toggleList: '/icons/toggle-list.svg',
selectArrow: '/icons/select-arrow.svg',
};
const viewerStyle = {
'--pdfjs-multi-bg': '#0a0f1c',
'--pdfjs-multi-texture': 'none',
'--pdfjs-multi-text': '#e2e8f0',
'--pdfjs-multi-surface': 'rgba(15, 23, 42, 0.9)',
'--pdfjs-multi-surface-active': 'rgba(30, 41, 59, 0.92)',
'--pdfjs-multi-muted-text': '#94a3b8',
} as const;
const rendererStyle = {
'--pdfjs-multi-controls-bg': '#0f172a',
'--pdfjs-multi-controls-text': '#f8fafc',
'--pdfjs-multi-control-icon-size': '18px',
'--pdfjs-multi-select-text': '#f8fafc',
} as const;
const HighContrastViewer = () => (
`css
.theme-contrast .renderer-controls {
/ renderer-controls defines its own icon vars, so inherit in themed skins /
--pdfjs-multi-control-icon-zoom-out: inherit;
--pdfjs-multi-control-icon-zoom-in: inherit;
--pdfjs-multi-control-icon-rotate-left: inherit;
--pdfjs-multi-control-icon-rotate-right: inherit;
--pdfjs-multi-control-icon-download: inherit;
}.theme-contrast .dropdown-toolbar-container {
--pdfjs-multi-select-icon: inherit;
}
`Available variables (non-exhaustive):
| Scope | Variable | Default | Purpose |
| --- | --- | --- | --- |
| MultiViewer |
--pdfjs-multi-bg | #404040 | Base background |
| MultiViewer | --pdfjs-multi-text | #fff | Text color |
| MultiViewer | --pdfjs-multi-surface | #565656 | List background |
| MultiViewer | --pdfjs-multi-surface-active | #474747 | Active list item |
| MultiViewer | --pdfjs-multi-muted-text | #cecece | Metadata text |
| MultiViewer | --pdfjs-multi-divider | rgba(53, 52, 52, 0.81) | List divider |
| MultiViewer | --pdfjs-multi-list-width | 300px | List width |
| MultiViewer | --pdfjs-multi-list-item-padding | 25px 35px | List item padding |
| MultiViewer | --pdfjs-multi-option-bar-height | 33px | Option bar height |
| MultiViewer | --pdfjs-multi-option-bar-padding | 4px 2px 4px 10px | Option bar padding |
| MultiViewer | --pdfjs-multi-min-height | 360px | Minimum viewer height |
| Shared | --pdfjs-multi-texture | url("./assets/texture.png") | Background texture |
| Controls | --pdfjs-multi-controls-bg | #474747 | Controls background |
| Controls | --pdfjs-multi-controls-text | hsla(0, 0%, 100%, 0.8) | Controls text |
| Controls | --pdfjs-multi-control-icon-zoom-out | image-set(...) | Zoom out icon |
| Controls | --pdfjs-multi-control-icon-zoom-in | image-set(...) | Zoom in icon |
| Controls | --pdfjs-multi-control-icon-rotate-left | image-set(...) | Rotate left icon |
| Controls | --pdfjs-multi-control-icon-rotate-right | image-set(...) | Rotate right icon |
| Controls | --pdfjs-multi-control-icon-download | image-set(...) | Download icon |
| Controls | --pdfjs-multi-control-icon-toggle-list | image-set(...) | Toggle list icon |
| Select | --pdfjs-multi-select-icon | url("./assets/toolbarButton-menuArrows.png") | Select icon |$3
Use the
icons prop to override the default PNGs. Values can be plain
URLs (wrapped as url(...)) or full CSS values (e.g. image-set(...) or
url(...)).`tsx
import type { IconConfig } from 'react-pdfjs-multi';const icons: IconConfig = {
zoomIn: '/icons/zoom-in.svg',
zoomOut: '/icons/zoom-out.svg',
rotateLeft: '/icons/rotate-left.svg',
rotateRight: '/icons/rotate-right.svg',
download: '/icons/download.svg',
toggleList: '/icons/toggle-list.svg',
selectArrow: '/icons/select-arrow.svg',
texture: 'none',
};
`If you want the same icon set applied to the renderer controls, pass
rendererIcons to PdfMultiViewer (or use icons directly on PdfRenderer).$3
The MultiViewer allows you to pass an array of source strings or objects (optionally with a custom
title) and it lazy-loads PDFs by default, showing the renderer as soon as the active PDF is loaded.
The list is guarded by the toggle in the option bar; on widths between ~330px and ~667px it switches into an overlay so the renderer keeps the extra space, and selecting a document when the list is overlaid closes it automatically. The viewer also records zoom, rotation, and scroll positions per document via the embedded pdfChangeHook, so revisiting a PDF restores the position you left it in.Props:
| Name | Required | Default | Type | Description |
| --------------- | -------- | ------- | --------------- | -------------------------------------------------------------- |
| pdfs | true | | {array} | Strings or
{ source, title? } objects for each PDF |
| autoZoom | | true | {boolean} | enables/disables autoZoom on component mount and window resize |
| className | | | {string} | Adds a class to the outer viewer container |
| controls | | true | {boolean} | enables/disables controls to e.g. change renderer zoom |
| icons | | | {IconConfig} | Override default icons and texture |
| rendererIcons | | | {IconConfig} | Override renderer control icons |
| rendererClassName | | | {string} | Adds a class to the renderer container |
| rendererStyle | | | {CSSProperties} | Inline styles for the renderer container |
| i18nData | | {}\* | {I18nData} | An object of translated strings, default language is en |
| startIndex | | 0 | {number} | first pdf to display using array index |
| lazyLoad | | true | {boolean} | load only the active PDF until a list item is selected |
| initialLoadIndex| | startIndex | {number|string} | which PDF to prefetch on mount when lazyLoad is enabled |
| style | | | {CSSProperties} | Inline styles for the outer viewer container |
| workerSrc | | | {string} | pdf.js worker URL |When
lazyLoad is enabled (the default), you can use initialLoadIndex to prime a different PDF than the one shown first, and PdfMultiViewer already passes its own pdfChangeHook to PdfRenderer so zoom/rotation/scroll state is cached automatically; you only need to provide your own hook if you want that data for something else.$3
To be able to use different i18n libraries eg. i18next or react-intl you can pass an i18n object with translated strings to the component.
\*defaults:
`javascript
{
// Viewer
loading: 'Loading...',
pages: 'Pages',
// Renderer
zoom: 'Automatic zoom',
originalSize: 'Original size',
rotateLeft: 'Rotate left',
rotateRight: 'Rotate right',
scaleUp: 'Scale up',
scaleDown: 'Scale down',
download: 'Download',
}
``If you like to implement your own custom multi renderer logic you can use the PdfRenderer component.
Props:
| Name | Required | Default | Type | Description |
| ------------- | -------- | ------- | ------------------ | -------------------------------------------------------------- |
| pdfDoc | true | | {PDFDocumentProxy} | A proxy of the pdf document to display |
| autoZoom | | true | {boolean} | enables/disables autoZoom on component mount and window resize |
| className | | | {string} | Adds a class to the renderer container |
| controls | | true | {boolean} | enables/disables controls to e.g. change renderer zoom |
| downloadBtn | | true | {boolean} | enables/disables download button |
| icons | | | {IconConfig} | Override default icons and texture |
| i18nData | | {}\* | {I18nDataRenderer} | An object of translated strings, default language is en |
| style | | | {CSSProperties} | Inline styles for the renderer container |
| zoom | | null | {number} | Initial Zoom |
| rotation | | null | {number} | Initial Rotation |
| scrollTop | | null | {number} | Initial ScrollTop |
| scrollLeft | | null | {number} | Initial ScrollLeft |
| activeIndex | | null | {number} | Is required whenn the pdfChangeHook is used |
| pdfChangeHook | | null | {func} | Callback function which gets a position object |
[1]: https://mozilla.github.io/pdf.js/
[2]: https://optimisticupdate.github.io/react-pdfjs-multi/