COMING SOON: Fall 2024 Release
By Apryse | 2024 Oct 14
8 min
Tags
pdf viewer
react
react pdf viewer
In this blog, we will cover how to build a customizable and feature-rich PDF Viewer in React. We will be using the Apryse SDK - Apryse’s rendering engine - and building a custom UI to display PDFs, scroll pages, zoom, and search with annotation and highlighting support.
For many users though, the open-source and easily customizable Webviewer-UI might be more appropriate - with many features straight out of the box, so you may want to check those out.
OK! If you are still reading then we'll go through the manual steps of setting up the UI.
We’ll assume that you already have a Node environment setup and you have a React app up that you want to add WebViewer to.
Alternatively, you can also just create a new React app (after setting up a Node environment), by running:
npm create vite@latest
then following the instructions.
If you are new to Vite and prefer a video, then see the first part of this video.
OK! Let’s get started adding PDF rendering to our app!
First, we need a robust rendering engine that will display PDFs. There are several open-source rendering engines available such as PDF.js and PDFium, however in this article we will use Apryse’s proprietary engine since it offers so much functionality.
Apryse’s proprietary engine comes with a lot of functionality, straight out-of-the-box. In addition to being great at rendering PDFs, it also supports:
Almost all functionality is performed directly within the browser (using WASM), making it extremely fast and keeping your documents secure rather than them being transferred over the internet and processed on a remote server.
Probably the easiest way to get the Apryse rendering engine is to run:
npm i @pdftron/webviewer
Once the package has been installed, copy the `public/core`
contents located in your `node_modules/@pdftron/webviewer/public/core`
to the `/public/webviewer/core`
folder in your React app.
We need to do this because the library runs in the browser, so it needs to be accessible from the client-side code that will run there.
Location of copied WebViewer files in your React project
Now, you can reference the necessary scripts for rendering between the <body> tags of `public/index.html`, similarly to how it is done in the GitHub repository for this project: https://github.com/PDFTron/webviewer-custom-ui/blob/master/public/index.html#L29
<script src="./webviewer/core/webviewer-core.min.js"></script>
<script src="./webviewer/core/pdf/PDFNet.js"></script>
Now, you should be able to reference it wherever you are building the PDF Viewer.
If you are using React 18 (which is the current default version at the time of writing this article), then it is likely to implement StrictMode which causes the React Components to render twice during development.
While that does not occur with production builds, it can be very confusing during development, so you may wish to remove the <React.StrictMode> element from main.jsx.
You can now create a reusable PDF Viewer component in your React app.
In our example, though, we will keep things simple and just add the code directly to `App.js`.
Let’s add where the PDF viewer will be rendered,as well as some classes and ids for styling. This is done in the return method of your component or App.js:
return (
<div className="App">
<div id="main-column">
<div className="flexbox-container" id="scroll-view" ref={scrollView}>
<div id="viewer" ref={viewer}></div>
</div>
</div>
</div>
);
Next insert a `useEffect` in App.js (which should only run once upon rendering the component - but see the warning about StrictMode), and add the following:
function App() {
const viewer = useRef(null);
const scrollView = useRef(null);
const [documentViewer, setDocumentViewer] = useState(null);
const [annotationManager, setAnnotationManager] = useState(null);
useEffect(() => {
const Core = window.Core;
Core.setWorkerPath('/webviewer/core');
Core.enableFullPDF();
const docViewer = new Core.DocumentViewer();
docViewer.setScrollViewElement(scrollView.current);
docViewer.setViewerElement(viewer.current);
docViewer.enableAnnotations();
docViewer.loadDocument('https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf');
docViewer.addEventListener('documentLoaded', () => {
// Store the annotation manager for later use
setAnnotationManager(docViewer.getAnnotationManager());
});
setDocumentViewer(docViewer);
}, []);
// return method....
}
Okay, what is happening there?
Now, let’s add some CSS, to make sure the viewer looks its best. Inside of App.css (or your component’s CSS), add the following and do not forget to import it:
:root {
--pdf-background: var(--grey-2);
--tools-header-background: var(--grey-3);
}
.App {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
overflow: hidden;
}
.App .header {
width: 100%;
height: 100px;
padding: 8px 8px 8px 16px;
box-sizing: border-box;
background: #00a5e4;
font-size: 1.2em;
line-height: 44px;
color: white;
}
#scroll-view {
bottom: 0;
/* leave room for 100px header */
height: 100%;
width: 100%;
overflow: auto;
flex: 3;
background-color: var(--pdf-background);
}
#viewer {
margin: auto;
}
#main-column {
flex-direction: row;
flex: 3;
}
#tools {
display:flex;
flex-direction: row;
justify-content: center;
background-color: var(--tools-header-background);
}
#tools button {
background-color: var(--tools-header-background);
fill: #757575;
width: fit-content;
height: 35px;
margin: 5px;
cursor: pointer;
}
#tools button:hover {
background-color: #c7d2dd;
border-radius: 4px;
}
.flexbox-container {
display: flex;
}
We also need to add some css to index.css, just to set the styles the way that we want.
:root {
--grey-2: #F1F3F5;
--grey-3: #E7EBEE;
}
html, body, #root {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
.webviewer {
flex: 1;
margin: 8px;
-webkit-box-shadow: 1px 1px 10px #999;
box-shadow: 1px 1px 10px #999;
}
At this point, we are ready to start our React app and see the PDF render:
npm run dev
WebViewer running in the browser
If you ran into any trouble, or something is going wrong, just refer to our GitHub sample - it's based on create-react-app rather than vite, but the principles are the same. If you are still stuck, chat with us on Discord.
If you are looking for a pre-built UI that is battle-tested by millions of developers, check out our other blog: https://apryse.com/blog/embed-react-pdf-viewer.
The next step, in this demo, is to allow users to draw annotations and comment on a PDF. Thanks to the Apryse Core rendering engine, WebViewer comes with out-of-the-box support for all annotation types supported in Adobe Reader. So, in this section, we are going to add a button to the UI that allows us to draw rectangles on a PDF document.
Inside the return method for App.js or your component, add the button tag just before the scroll view:
return (
<div className="App">
<div id="main-column">
<div className="center" id="tools">
<button id='btn' onClick={createRectangle}>Rectangle</button>
</div>
<div className="flexbox-container" id="scroll-view" ref={scrollView}>
<div id="viewer" ref={viewer}></div>
</div>
</div>
</div>
);
Let’s now add the code that will set the tool to be a rectangle whenever that button is pressed.
Somewhere between useEffect and the return statement, add the following:
const createRectangle = () => {
documentViewer.setToolMode(documentViewer.getTool(window.Core.Tools.ToolNames.RECTANGLE));
};
We have already added some basic CSS for the button, but feel free to play around with it to get it to match your overall theme.
Now, the button press will allow users to add rectangle annotations. Restart the app and try it yourself.
Clicking on the Rectangle button allows the user to add annotations.
At this point, you can add more tool support, page manipulation, search, redaction and more. Check out the full sample available on GitHub and refer to our guides for more information.
Alternatively checkout the open-source WebViewer UI which gives you huge functionality straight out of the box. It now has a modular UI that gives you rapid customization abilities that will allow you to be WCAG 2.1 compliant.
This article was updated in October 2024.
Tags
pdf viewer
react
react pdf viewer
Apryse
Related Products
WebViewer
Share this post
PRODUCTS
Enterprise
Small Business
Popular Content