Adding React viewers is a common requirement for web apps. With React, there’s the react-pdf-viewer
package that lets us add PDF viewers to React apps easily.
In this article, we’ll look at how to use it to add a PDF viewer to our React app.
Installation
We can install it by running:
npm install @phuocng/react-pdf-viewer
Basic Usage
After installing it, we can use it as follows:
import React from "react";
import Viewer, { Worker } from "@phuocng/react-pdf-viewer";
import "@phuocng/react-pdf-viewer/cjs/react-pdf-viewer.css";
export default function App() {
return (
<div className="App">
<Worker workerUrl="https://unpkg.com/pdfjs-dist@2.2.228/build/pdf.worker.min.js">
<div style={{ height: "750px" }}>
<Viewer fileUrl="dummy.pdf" />
</div>
</Worker>
</div>
);
}
In the code above, we included the CSS file that comes with the package, and the Viewer
for opening the PDF viewer and the Worker
component for loading the PDF specified in the fileUrl
prop as in the background.
We have to include the workerUrl
prop with that URL so that the worker in that location is run.
Then we’ll get a PDF viewer that has zoom in and out controls, page navigation, document properties, and download options.
In a Create React App project, static files like PDFs should be in the public
folder so that it can be loaded. Also, PDFs have to be in the same domain as the React app so that we won’t get CORS errors.
Options
There’re options for customization. We can change the layout of the sidebar, toolbar, and replace default controls with React components of our choice.
Layout options available include:
isSidebarOpened
– boolean to indicate whether we want the sidebar to open or notmain
– the main part of the viewer (aSlot
component object)toolbar
– toolbar part (aRenderToolbar
component object)sidebar
–Slot
object to define the sidebar of the viewer
For instance, we can write the following code to add a sidebar to display pages and a layout
component do define a layout for our PDF viewer as follows:
import React from "react";
import Viewer, {
Worker,
} from "@phuocng/react-pdf-viewer";
import "@phuocng/react-pdf-viewer/cjs/react-pdf-viewer.css";
export default function App() {
const renderToolbar = (toolbarSlot) => {
return (
<div
style={{
alignItems: 'center',
display: 'flex',
width: '100%',
}}
>
<div
style={{
alignItems: 'center',
display: 'flex',
}}
>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.searchPopover}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.previousPageButton}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.currentPageInput} / {toolbarSlot.numPages}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.nextPageButton}
</div>
</div>
<div
style={{
alignItems: 'center',
display: 'flex',
flexGrow: 1,
flexShrink: 1,
justifyContent: 'center',
}}
>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.zoomOutButton}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.zoomPopover}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.zoomInButton}
</div>
</div>
<div
style={{
alignItems: 'center',
display: 'flex',
marginLeft: 'auto',
}}
>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.fullScreenButton}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.openFileButton}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.downloadButton}
</div>
<div style={{ padding: '0 5px' }}>
{toolbarSlot.moreActionsPopover}
</div>
</div>
</div>
);
};
const layout = (
isSidebarOpened,
main,
toolbar,
sidebar
) => {
return (
<div
style={{
border: '1px solid rgba(0, 0, 0, .3)',
display: 'grid',
gridTemplateAreas: "'toolbar toolbar' 'sidebar main'",
gridTemplateColumns: '30% 1fr',
gridTemplateRows: '40px calc(100% - 40px)',
height: '100%',
overflow: 'hidden',
width: '100%',
}}
>
<div
style={{
alignItems: 'center',
backgroundColor: '#EEE',
borderBottom: '1px solid rgba(0, 0, 0, .1)',
display: 'flex',
gridArea: 'toolbar',
justifyContent: 'center',
padding: '4px',
}}
>
{toolbar(renderToolbar)}
</div>
<div
style={{
borderRight: '1px solid rgba(0, 0, 0, 0.2)',
display: 'flex',
gridArea: 'sidebar',
justifyContent: 'center',
}}
>
{sidebar.children}
</div>
<div
{...main.attrs}
style={Object.assign({}, {
gridArea: 'main',
overflow: 'scroll',
}, main.attrs.style)}
>
{main.children}
</div>
</div>
);
};
return (
<Worker workerUrl="https://unpkg.com/pdfjs-dist@2.2.228/build/pdf.worker.min.js">
<Viewer
fileUrl='dummy.pdf'
layout={layout}
/>
</Worker>
);
}
The default layout is the following:
┌───────────┬───────────┐
│ toolbar │ toolbar │
├───────────┼───────────┤
│ sidebar │ main │
└───────────┴───────────┘
In the code above, we reference those parts in the places we wish to place in the layout
component. The children
attribute has the parts of each component. attrs
has the default props of each component, which we can change.
The renderToolbar
function is a higher-order component that takes a toolbarSlot
prop, which has the parts of the toolbar and we can place them accordingly according to our needs. In the example above, we put them in different divs and added our own styles to each div.
The grid above is a CSS grid, so we can modify the layout as we please and it’ll will in all modern browsers.
Those pats are passed in as props in the layout
component. So we can reference them directly from the parameters of layout
.
Conclusion
The react-pdf-viewer
package is a very useful PDF viewer that’s designed with both performance and usability in mind. The default layout and controls are already very good. Performance comes from loading PDFs in the background with a web worker.
It’s also very customizable, we can define a layout component that has toolbar
, sidebar
and main
as props and then we can customize them as we wish.