Unlock the Power of Direct PDF Editing with WebViewer 10.7

How To Build a Customizable React PDF Viewer

By Andrey Safonov | 2023 Jun 16

Sanity Image
Read time

5 min

In this blog, we will cover how to build a customizable and feature-rich PDF Viewer in React. We will be using Apryse’s rendering engine, and building a custom UI to display PDFs, scroll pages, zoom, and search with annotation and highlighting support.

We’ll assume that you already have a Node environment setup and you have a React app up and running. If you do not, you can grab the source code to this blog on our GitHub https://github.com/PDFTron/webviewer-custom-ui. You can also just create a new React app after setting up a Node environment, by running:

npx create-react-app my-app
cd my-app

For more info on create-react-app refer to the official documentation.

Let’s get started!

Install a PDF Rendering Engine

First, we need a robust rendering engine that will display PDFs. There are several open-source rendering engines available like PDF.js and PDFium, however, Apryse’s proprietary engine comes with a lot of functionality out-of-the-box for not only rendering PDFs, but also:

Probably the easiest way to get theApryse rendering engine is to run:

npm i @pdftron/webviewer

Then copy the `public/core` contents located in your `node_modules/@pdftron/webviewer/public/core` to the `/public/webviewer` folder in your React app.

Now, you can reference the necessary scripts for rendering between the <body> tags of `public/index.html`, , similarly to how we did it in our GitHub repository for this project: https://github.com/PDFTron/webviewer-custom-ui/blob/master/public/index.html#L29

<script src="%PUBLIC_URL%/webviewer/core/webviewer-core.min.js"></script>

<script src="%PUBLIC_URL%/webviewer/core/pdf/PDFNet.js"></script>

Now, you should be able to reference it wherever you are building the PDF Viewer.

Create a React PDF Viewer Component

You can now create a reusable PDF Viewer component in your React app. In our example, we kept things simple and just added the code directly to `App.js`:https://github.com/PDFTron/webviewer-custom-ui/blob/master/src/App.js.

Let’s add where the PDF will be rendered. To begin, let’s add a couple of references that we will need inside the component or App.js:

const App = () => {
const viewer = useRef(null);
const scrollView = useRef(null);

In the return method for App.js or your component, add the following:

return (

<div className="App">

<div id="scroll-view" ref={scrollView}>

<div id="viewer" ref={viewer}></div>



And then insert into `useEffect` in App.js, which should only run once upon rendering the component, you can add the following:

const App = () => {
const viewer = useRef(null);
const scrollView = useRef(null);
const [documentViewer, setDocumentViewer] = useState(null);
const [annotationManager, setAnnotationManager] = useState(null);
useEffect(() => {
const Core = window.Core;
const documentViewer = new Core.DocumentViewer();
documentViewer.loadDocument(' https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf');

documentViewer.addEventListener('documentLoaded', () => {
}, []);

Okay, what is happening there?

  1. We load the Core rendering engine from Apryse.
  2. We set the path to the resources necessary for document rendering.
  3. We create a new document viewer and point it to the div element we created earlier.
  4. We then enable annotations and load our PDF from an s3 bucket, however, you can point it to local files as well in `public` folder.
  5. After that, we set the document viewer to the one we created.
  6. And then we wait for an event that lets us know the viewer is ready to go and the document is loaded.

Provide a best-in-class viewer experience, best quality and performance across web, mobile, and desktop applications with Apryse.

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:

#scroll-view {
bottom: 0;
height: 100%;
width: 100%;
overflow: auto;
#viewer {
margin: auto;


At this point, we are ready to start our React app and see the PDF render:

npm run start

If you ran into any trouble, or something is going wrong, just refer to our GitHub sample If you are still stuck, chat with us on Discord using the link at the end of this article.

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.

Add Annotation Support to Your PDF Viewer

The next is step is to allow users to draw annotations and comment on a PDF. Thanks to the Apryse Core rendering engine, WebViewercomes 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 following next to the scroll view:

return (

<div className="App">

<button onClick={createRectangle}>Rectangle</button>

<div id="scroll-view" ref={scrollView}>

<div id="viewer" ref={viewer}></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 = () => {



Now, the button press will allow users to add rectangle annotations. Restart the app and try it yourself. The CSS might not be pretty, but play around and get it to look right for your overall theme.

Learn more about font substitution when converting from Office to PDF.

Next Steps

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. You can also chat with us on our Discord.

Sanity Image

Andrey Safonov

Director of Product

LinkedIn link

Share this post