AVAILABLE NOW: Spring 2025 Release
By Dustin Riley | 2020 Jul 21
4 min
Tags
pdf.js
pdf viewer
In this article (a four-minute read), you’ll learn how to quickly build a PDF viewer with Bootstrap and PDF.js, a popular open-source PDF viewer.
Prerequisites
We will be using http-server to run our website locally. To install http-server
, run the following command from the Node.js command line:
npm install --global http-server
Now we will create a simple project directory for our Bootstrap project. In your project folder create a folder structure that looks something like this:
pdfjs-bootstrap-project
├── public
│ ├── js
│ │ ├── index.js
│ ├── css
│ │ ├── index.css
│ ├── lib
│ │ ├── ...
│ ├── index.html
│ ├── form.html
│ ├── pdftron.html
│ └── ...
└── ...
All these files will be empty for now. We will populate them with content shortly.
We will now integrate the open-source PDF.js library into our project to render a PDF inside our app. We will start by downloading the latest stable release from GitHub and then extracting the contents into the public/lib
folder.
We will also need a PDF file to view, which we will place in the web folder. You can use your own or download one from here.
The new file structure in our project folder will look like the following:
pdfjs-bootstrap-project
├── public
│ ├── js
│ │ ├── index.js
│ ├── css
│ │ ├── index.css
│ ├── lib
│ │ ├── ui
│ │ │ ├── ...
│ │ ├── core
│ │ │ ├── ...
│ │ └── webviewer.min.js
│ ├── index.html
│ ├── form.html
│ ├── pdftron.html
│ └── ...
└── ...
Next, we will make it so we can view the default viewer within our project. In the public/index.html
add the following:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap PDF.js Viewer</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<ul className="nav nav-tabs">
<li className="nav-item active">
<a className="nav-link" href="/">About</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/form">Form</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/pdftron">PDFTron</a>
</li>
<li className="nav-item disabled">
<a className="nav-link" href="#">Disabled</a>
</li>
</ul>
<div className="row">
<div style={{"height":"75vh"}} className="col-sm-6">
<iframe height="100%" width=100% src='lib/web/viewer.html?file=pdf-js-express-default-doc-v2.pdf#zoom=40'></iframe>
</div>
<div className="col-sm-6">
<div className="panel panel-info">
<div className="panel-heading">
<h3 className="panel-title">Bootstrap</h3>
</div>
<div className="panel-body">
Bootstrap is a free and open-source CSS framework directed at responsive,
mobile-first front-end web development. It contains CSS- and JavaScript-based design
templates for typography, forms, buttons, navigation, and other interface components.
</div>
</div>
</div>
</div>
</body>
</html>
In the public/form.html
add the following:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap PDF.js Viewer</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link" href="/">About</a>
</li>
<li className="nav-item active">
<a className="nav-link" href="/form">Form</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/pdftron">PDFTron</a>
</li>
<li className="nav-item disabled">
<a className="nav-link " href="#">Disabled</a>
</li>
</ul>
<div className="row">
<div style={{"height":"75vh"}} className="col-sm-6">
<iframe height="100%" width=100% src='lib/web/viewer.html?file=form-demo.pdf#zoom=50'></iframe>
</div>
<div className="col-sm-6">
<div className="panel panel-info">
<div className="panel-heading">
<h3 className="panel-title">Rendering a PDF with Bootstrap isn't difficult using open-source libraries.</h3>
</div>
<div className="panel-body">
Building a PDF Viewer with Bootstrap is relatively straightforward,
but once you want to start annotating, signing, or filling forms,
you would have to implement these things yourself.
</div>
</div>
</div>
</div>
</body>
</html>
Also add in public/pdftron.html
the following:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap PDF.js Viewer</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link" href="/">About</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/form">Form</a>
</li>
<li className="nav-item active">
<a className="nav-link" href="/pdftron">PDFTron</a>
</li>
<li className="nav-item disabled">
<a className="nav-link" href="#">Disabled</a>
</li>
</ul>
<div className="row">
<div style={{"height":"75vh"}} className="col-sm-6">
<iframe height="100%" width=100% src='lib/web/viewer.html?file=demo.pdf#zoom=50'></iframe>
</div>
<div className="col-sm-6">
<div className="panel panel-info">
<div className="panel-heading">
<h3 className="panel-title">PDFTron WebViewer.</h3>
</div>
<div className="panel-body">
If you need high-fidelity rendering, increased reliability, and faster performance,
you could consider PDFTron WebViewer.
It’s a JavaScript PDF library that integrates
with with many frameworks and offers hundreds of features, like redaction, editing,
page manipulation, real-time document collaboration, digital signatures, and much more.
</div>
</div>
</div>
</div>
</body>
</html>
Make sure to change the PDF file names by adding custom PDFs to the lib/web
folder and change all of the .html
files by replacing the following with your PDF's file name:
<iframe height="100%" width=100% src='lib/web/viewer.html?file=<YOUR-FILE.pdf>'></iframe>
We are importing Bootstrap from a content delivery network by adding the following in the <head>
section:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
Bootstrap is a grid system that is based off of dividing the page into 12 columns so adding the class col-sm-6
means that the element will take up six of the 12 columns when the screen size is small (greater than 575px but less than 766px). It will continue to use 6 columns for screen sizes larger than this but when it is considered to be extra small (less than 576px) it will default to using all 12 columns. This is because we haven't specified any behaviour for extra small screen sizes. You can learn more about Bootstrap grid here.
Adding nav nav-tabs
classes to a <ul>
styles the tabs automatically to look like a header. To add items to the header just add the class nav-item
to the child <li>
. We can also display which page we are on by adding the class active
to the <li>
as well. Also adding the class disabled
will make it look like the tab is inaccessible. You can read more about other Bootstrap tab styling here.
Similarly adding the panel panel-info
classes creates the panel that is to the right of the PDF.js viewer. Adding the classes panel-heading
and panel-title
to the children of the panel element adds the heading and title respectively. You can also dictate the body by adding the panel-body
class. If you want to learn more about Bootstrap panels you can read about that here.
Navigate to http://localhost:8080/ and you’ll see the default viewer and some navigation tabs which change the PDF being displayed:
As a final step, we will reorganize the toolbar by moving elements around, removing buttons, and changing the icons.
Let’s open public/lib/web/viewer.html
and add the following to the <head>
section:
<script src="customToolbar.js"></script>
Next, we’ll create customToolbar.js
inside the public/lib/web
folder and add the following code:
let sheet = (function() {
let style = document.createElement("style");
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
return style.sheet;
})();
function editToolBar(){
//when the page is resized, the viewer hides and move some buttons around.
//this function forcibly show all buttons so none of them disappear or re-appear on page resize
removeGrowRules();
/* Reorganizing the UI */
// the 'addElemFromSecondaryToPrimary' function moves items from the secondary nav into the primary nav
// there are 3 primary nav regions (toolbarViewerLeft, toolbarViewerMiddle, toolbarViewerRight)
//adding elements to left part of toolbar
addElemFromSecondaryToPrimary('pageRotateCcw', 'toolbarViewerLeft')
addElemFromSecondaryToPrimary('pageRotateCw', 'toolbarViewerLeft')
addElemFromSecondaryToPrimary('zoomIn', 'toolbarViewerLeft')
addElemFromSecondaryToPrimary('zoomOut', 'toolbarViewerLeft')
//adding elements to middle part of toolbar
addElemFromSecondaryToPrimary('previous', 'toolbarViewerMiddle')
addElemFromSecondaryToPrimary('pageNumber', 'toolbarViewerMiddle')
addElemFromSecondaryToPrimary('numPages', 'toolbarViewerMiddle')
addElemFromSecondaryToPrimary('next', 'toolbarViewerMiddle')
//adding elements to right part of toolbar
addElemFromSecondaryToPrimary('secondaryOpenFile', 'toolbarViewerRight')
/* Changing icons */
changeIcon('previous', 'icons/baseline-navigate_before-24px.svg')
changeIcon('next', 'icons/baseline-navigate_next-24px.svg')
changeIcon('pageRotateCcw', 'icons/baseline-rotate_left-24px.svg')
changeIcon('pageRotateCw', 'icons/baseline-rotate_right-24px.svg')
changeIcon('viewFind', 'icons/baseline-search-24px.svg');
changeIcon('zoomOut', 'icons/baseline-zoom_out-24px.svg')
changeIcon('zoomIn', 'icons/baseline-zoom_in-24px.svg')
changeIcon('sidebarToggle', 'icons/baseline-toc-24px.svg')
changeIcon('secondaryOpenFile', './icons/baseline-open_in_browser-24px.svg')
/* Hiding elements */
removeElement('secondaryToolbarToggle')
removeElement('scaleSelectContainer')
removeElement('presentationMode')
removeElement('openFile')
removeElement('print')
removeElement('download')
removeElement('viewBookmark')
}
function changeIcon(elemID, iconUrl){
let element = document.getElementById(elemID);
let classNames = element.className;
classNames = elemID.includes('Toggle')? 'toolbarButton#'+elemID :
classNames.split(' ').join('.');
classNames = elemID.includes('view')? '#'+elemID+'.toolbarButton' : '.'+classNames
classNames+= "::before";
addCSSRule(sheet, classNames, `content: url(${iconUrl}) !important`, 0)
}
function addElemFromSecondaryToPrimary(elemID, parentID){
let element = document.getElementById(elemID);
let parent = document.getElementById(parentID);
element.style.minWidth = "0px";
element.innerHTML =''
parent.append(element);
}
function removeElement(elemID){
let element = document.getElementById(elemID);
element.parentNode.removeChild(element);
}
function removeGrowRules(){
addCSSRule(sheet, '.hiddenSmallView *', 'display:block !important');
addCSSRule(sheet, '.hiddenMediumView', 'display:block !important');
addCSSRule(sheet, '.hiddenLargeView', 'display:block !important');
addCSSRule(sheet, '.visibleSmallView', 'display:block !important');
addCSSRule(sheet, '.visibleMediumView', 'display:block !important');
addCSSRule(sheet, '.visibleLargeView', 'display:block !important');
}
function addCSSRule(sheet, selector, rules, index) {
if("insertRule" in sheet) {
sheet.insertRule(selector + "{" + rules + "}", index);
}
else if("addRule" in sheet) {
sheet.addRule(selector, rules, index);
}
}
window.onload = editToolBar
The PDF.js primary toolbar is broken down into 3 regions:
The secondary toolbar is accessed via the chevron icon in the right region:
We can move elements from the secondary toolbar into the left, middle, or right regions of the primary toolbar with the addElemFromSecondaryToPrimary
function in customToolbar.js
. For example, uncommenting this line will move the counter-clockwise rotation tool to the left region of the primary toolbar:
addElemFromSecondaryToPrimary('pageRotateCcw', 'toolbarViewerLeft')
If you wanted to move pageRotateCcw
to the middle region instead, you’d replace toolbarViewerLeft
with toolbarViewerMiddle
, or toolbarViewerRight
for the right region. To move a different tool, replace the pageRotateCcw
ID with the element ID you want to move. (See below for a full list of element IDs.)
We can also hide elements like this:
removeElement('print')
removeElement('download')
To hide different elements, replace print
or download
with the element ID.
NOTE: Hiding the download and print buttons is not a bulletproof way to protect our PDF, because it’s still possible to look at the source code to find the file. It just makes it a bit harder.
We can also customize the icons for various tools by swapping out the SVG
file, like this:
changeIcon('previous', 'icons/baseline-navigate_before-24px.svg')
In the above example, previous
is the element ID, while icons/baseline-navigate_before-24px.svg
is the path to the tool icon.
And that’s it!
As you can see, rendering a PDF with Bootstrap isn't difficult using open-source libraries.
Building a PDF Viewer with Bootstrap is relatively straightforward, but once you want to start annotating, signing, or filling forms, you would have to implement these things yourself. See our PDF.js Build vs Buy and Guide to Evaluating PDF.js to learn more.
That’s where PDF.js Express comes in. It’s a commercial PDF.js viewer that wraps a React-based UI around the open-source PDF.js rendering engine and offers out-of-the-box features like annotations, form filling and e-signatures. It’s fully compatible with many frameworks -- check out the demo, and let us know what you think!
If you need high-fidelity rendering, increased reliability, and faster performance, you could consider PDFTron WebViewer. It’s a JavaScript PDF library that integrates with many frameworks and offers hundreds of features, like redaction, editing, page manipulation, real-time document collaboration, digital signatures, and much more. Check out the WebViewer demo.
If you have any questions about implementing PDF.js Express in your project, please contact us and we will be happy to help!
Tags
pdf.js
pdf viewer
Dustin Riley
Share this post
PRODUCTS
Platform Integrations
End User Applications
Popular Content