Blog Post

How to build a jQuery PDF viewer with PDF.js

Illustration: How to build a jQuery PDF viewer with PDF.js
Information

This article was first published in November 2021 and was updated in October 2024.

If you’re looking to integrate a PDF viewer into your website or web application, you can easily do so using PDF.js, an open source library developed by Mozilla. When combined with jQuery, which simplifies event handling and DOM manipulation, this solution becomes incredibly versatile and user-friendly.

In this blog post, you’ll learn how to build a fully functional jQuery PDF viewer that can load, navigate, zoom, and print PDF files — all using PDF.js and jQuery. This method is simple to set up and offers great flexibility for customization.

Overview of the tutorial

In this tutorial, the process is in two parts:

  1. Rendering and viewing a PDF in the browser using PDF.js.

  2. Building a fully featured PDF viewer with additional functionalities like zoom, navigation, and print, enhanced by the Nutrient jQuery PDF library.

The Nutrient jQuery PDF library offers several advantages over PDF.js, including:

  • A prebuilt and polished UI for an improved user experience
  • 15+ prebuilt annotation tools to enable document collaboration
  • Support for more file types with client-side PDF, MS Office, and image viewing
  • Dedicated support from engineers to speed up integration

Requirements

To get started, you’ll need:

  • Node.js

  • A package manager for installing the Nutrient library. You can use npm or Yarn. If you don’t want to use a package manager, you may choose to manually download the files into your project.

Information

When you install Node.js, npm is installed by default.

Building a simple PDF viewer with jQuery and PDF.js

Understanding PDF.js

PDF.js is an open source JavaScript library that renders PDF documents directly in the browser using HTML5’s <canvas> element. This section will show you how to use PDF.js along with jQuery to create a simple PDF viewer.

A primer on HTML5 canvas

The HTML5 <canvas> element is a 2D drawing surface used to render graphics, images, and other visual content. PDF.js uses this to render PDF files.

<canvas id="canvas"></canvas>

You’ll need to access the canvas context to render content, and jQuery simplifies this:

const $ctx = $('#canvas')[0].getContext('2d');

This gives you access to the 2D drawing API, which you’ll use to render the PDF onto the canvas.

By the end of this section, you’ll be able to:

  • View and display a PDF file in the browser.
  • Zoom in and out.
  • Navigate to a specific page.
  • Navigate to the next and previous pages.
Information

You can see the end result here and access the full code on GitHub.

Project setup

Before diving into the code, ensure you have the necessary libraries.

  1. PDF.js — This will help you render the PDF document into the browser.

  2. jQuery — Use this to handle user interactions and DOM manipulation.

  3. Font Awesome — For nice icons like arrows and zoom buttons.

Here’s the HTML file setup:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta
			name="viewport"
			content="width=device-width, initial-scale=1.0"
		/>
		<title>PDF Viewer in jQuery</title>
		<link
			rel="stylesheet"
			href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
			crossorigin="anonymous"
			referrerpolicy="no-referrer"
		/>
		<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
		<link rel="stylesheet" href="style.css" />
	</head>
	<body>
		<header>
			<ul class="navigation">
				<li class="navigation__item">
					<a href="#" class="previous round" id="prev_page">
						<i class="fas fa-arrow-left"></i>
					</a>
					<input type="number" value="1" id="current_page" />
					<a href="#" class="next round" id="next_page">
						<i class="fas fa-arrow-right"></i>
					</a>
					Page
					<span id="page_num">1</span>
					of
					<span id="page_count">1</span>
				</li>
				<li class="navigation__item">
					<button class="zoom" id="zoom_in">
						<i class="fas fa-search-plus"></i>
					</button>
					<button class="zoom" id="zoom_out">
						<i class="fas fa-search-minus"></i>
					</button>
				</li>
				<li class="navigation__item">
					<button class="print-button">
						<i class="fa-solid fa-print"></i>
					</button>
				</li>
			</ul>
		</header>
		<canvas id="canvas" class="canvas__container"></canvas>
		<script type="module" src="index.js"></script>
	</body>
</html>

Core features of the PDF viewer

This PDF viewer comes with several key features:

  1. Navigation — Move between pages with Next and Previous buttons.

  2. Zoom — Zoom in and out of the PDF pages.

  3. Page jumping — Jump directly to a specific page by typing the page number.

  4. Printing — Print the PDF document directly from the viewer.

JavaScript logic for the PDF viewer

Now, examine the JavaScript code that powers the PDF rendering and interaction:

const pdf = 'document.pdf';

// Import necessary modules from PDF.js.
import {
	GlobalWorkerOptions,
	getDocument,
} from 'https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.min.mjs';

// Set the worker source to the PDF.js worker.
GlobalWorkerOptions.workerSrc =
	'https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.worker.min.mjs';

const initialState = {
	pdfDoc: null, // PDF document object.
	currentPage: 1, // Current page number.
	pageCount: 0, // Total number of pages.
	zoom: 1, // Current zoom level.
};

// Render the current page to the canvas.
const renderPage = () => {
	initialState.pdfDoc
		.getPage(initialState.currentPage)
		.then((page) => {
			const canvas = $('#canvas')[0]; // Get canvas element.
			const ctx = canvas.getContext('2d'); // Get canvas context.
			const viewport = page.getViewport({
				scale: initialState.zoom,
			});

			canvas.height = viewport.height; // Set canvas height.
			canvas.width = viewport.width; // Set canvas width.

			// Render the PDF page into the canvas.
			const renderCtx = {
				canvasContext: ctx,
				viewport: viewport,
			};

			page.render(renderCtx);

			// Update the page number on the UI.
			$('#page_num').text(initialState.currentPage);
		});
};

// Load the PDF document.
getDocument(pdf)
	.promise.then((data) => {
		initialState.pdfDoc = data; // Save the PDF document.
		$('#page_count').text(initialState.pdfDoc.numPages); // Display total page count.

		renderPage(); // Render the first page.
	})
	.catch((err) => {
		alert(err.message); // Handle error if PDF fails to load.
	});

Key sections:

  1. Loading the PDF:

    • The getDocument function from PDF.js loads the PDF. You specify the file (in this case, document.pdf).

    • The promise function ensures the PDF is loaded before performing any operations.

  2. Rendering pages:

    • The renderPage() function renders the current page on a canvas.

    • Use getViewport() to adjust the zoom level and scale the page accordingly.

    • The canvasContext renders the content of the PDF page onto the canvas element on the webpage.

Adding navigation and zoom controls

Now, take a look at the jQuery logic for handling navigation and zoom controls:

// Show Previous Page.
const showPrevPage = () => {
	if (initialState.pdfDoc === null || initialState.currentPage <= 1)
		return;
	initialState.currentPage--;
	$('#current_page').val(initialState.currentPage); // Update current page input.
	renderPage(); // Render the previous page.
};

// Show Next Page.
const showNextPage = () => {
	if (
		initialState.pdfDoc === null ||
		initialState.currentPage >= initialState.pdfDoc.numPages
	)
		return;
	initialState.currentPage++;
	$('#current_page').val(initialState.currentPage); // Update current page input.
	renderPage(); // Render the next page.
};

// Zoom In.
const zoomIn = () => {
	if (initialState.pdfDoc === null) return;
	initialState.zoom *= 1.25; // Increase the zoom factor.
	renderPage(); // Rerender the current page with updated zoom.
};

// Zoom Out.
const zoomOut = () => {
	if (initialState.pdfDoc === null) return;
	initialState.zoom *= 0.8; // Decrease the zoom factor.
	renderPage(); // Rerender the current page with updated zoom.
};
  • NavigationshowPrevPage() and showNextPage() update the currentPage variable and rerender the page.

  • ZoomzoomIn() and zoomOut() adjust the zoom factor (initialState.zoom) and re-render the page at the new zoom level.

Adding event listeners with jQuery

Now, you need to handle the events triggered by the user:

// Bind events using jQuery.
$('#prev_page').on('click', showPrevPage);
$('#next_page').on('click', showNextPage);
$('#zoom_in').on('click', zoomIn);
$('#zoom_out').on('click', zoomOut);

// Keypress Event for jumping to a page.
$('#current_page').on('keypress', (event) => {
	if (initialState.pdfDoc === null) return;

	if (event.keyCode === 13) {
		let desiredPage = parseInt($('#current_page').val());
		initialState.currentPage = Math.min(
			Math.max(desiredPage, 1),
			initialState.pdfDoc.numPages,
		);
		$('#current_page').val(initialState.currentPage);
		renderPage();
	}
});

// Optional Print Support.
$('.print-button').on('click', () => {
	window.print();
});
  • Event bindings — jQuery is used to bind click events to the buttons. Each event handler corresponds to a specific function (e.g. showPrevPage, zoomIn).

  • Keypress for jumping pages — If the user types a number in the input field and presses Enter, the page jumps to the desired page.

Challenges of working with PDF.js

PDF.js is an open source library that provides basic PDF viewing functionality right out of the box. However, it has some limitations that can become apparent when working with more advanced use cases or large, complex documents.

  • Limited documentation and an unstructured API design, leading to longer development times when customizing the PDF.js viewer.
  • Significant time and developer resources are required to add advanced features — like annotations, document editing, and eSignatures — to the viewer.
  • Performance issues with large or complex PDFs, leading to slow rendering and poor user experience. Handling large documents or highly detailed PDFs often leads to performance bottlenecks.
  • Challenges in maintaining and fixing the viewer when a new version of PDF.js is released, often requiring additional development work.

Benefits of Nutrient for PDF viewing and annotations

While PDF.js is a great tool for basic PDF rendering, Nutrient offers many advanced features out of the box, designed to meet the needs of businesses and teams that require a robust PDF viewer and editor. Nutrient’s powerful features make it ideal for industries like legal, education, and finance, where document collaboration and annotations are critical.

  • Save time with a well-documented API, making it easier to customize the UI to your specific requirements. This ensures smoother development cycles and quicker product releases.
  • Utilize 15+ prebuilt annotation tools for drawing, circling, highlighting, commenting, and adding notes to documents. These features are particularly beneficial for industries like legal and education, where collaboration and feedback are essential.
  • View PNG, JPG, TIFF, and MS Office documents directly on the client side without needing server deployment. This allows seamless integration into existing workflows that require different file formats.
  • Easily add features like PDF editing, digital signatures, form filling, real-time document collaboration, and more, streamlining workflows for teams, and increasing productivity.
  • Benefit from enterprise-level support, with dedicated engineers available to help with implementation, ensuring fast resolution of any issues and offering a more reliable experience for businesses.
  • Nutrient offers seamless integration with various platforms, making it a versatile solution for different tech stacks.

Code snippets and setup

Getting started with Nutrient for jQuery is straightforward. This tutorial will guide you through the setup process — from installing the library, to embedding the PDF viewer into your project.

  1. Install the Nutrient library with npm:

npm install pspdfkit
yarn add pspdfkit
  1. Copy the necessary library files to your assets directory for the viewer to function correctly:

cp -R ./node_modules/pspdfkit/dist/ ./assets/

Ensure your assets directory contains the pspdfkit.js file and the necessary library assets for smooth integration.

Integrating Nutrient into your project

  1. Add the PDF document you want to display to your project’s directory. You can use the demo document as an example.

  2. Create an empty <div> element for the PDF viewer:

<div id="pspdfkit" style="width: 100%; height: 100vh;"></div>
  1. Include the jQuery CDN in your HTML:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  1. Load pspdfkit.js:

<script src="assets/pspdfkit.js"></script>
  1. Initialize Nutrient Web SDK:

<script>
	PSPDFKit.load({
		container: '#pspdfkit',
		document: 'document.pdf',
	})
		.then(function (instance) {
			console.log('PSPDFKit loaded', instance);
		})
		.catch(function (error) {
			console.error(error.message);
		});
</script>

Serving the website locally

Another option is to use the serve package as a simple HTTP server. Follow the steps below if you want to use that option.

  1. Install the serve package:

npm install --global serve
yarn global add serve
  1. Serve the contents of the current directory:

serve -l 8080 .

Navigate to http://localhost:8080 to view the website.

pspdfkit demo

If you want to download Nutrient manually or integrate it as a module, you can check out our jQuery getting started guide.

Visual comparison: PDF.js vs. Nutrient

Here’s a side-by-side comparison of the features of PDF.js vs. Nutrient:

Feature PDF.js Nutrient
Basic PDF viewing ✔️ Simple PDF rendering ✔️ Advanced rendering with better performance for large files
Annotations ❌ Limited or requires extensive custom development ✔️ 15+ built-in annotation tools for legal, educational, and business use
Document editing ❌ Not supported out of the box ✔️ Full editing, form filling, and digital signature capabilities
Performance ❌ Slower performance with large PDFs ✔️ High performance even with large and complex documents
Support ❌ Community support only ✔️ Enterprise-grade support with dedicated engineers
Integration ✔️ Easy to integrate with basic projects ✔️ Seamless integration with various tech stacks and platforms

Conclusion

PDF.js is a fantastic lightweight, open source solution for simple PDF rendering needs. It’s an excellent choice for projects where basic PDF viewing functionality is sufficient, and there are no performance or complex interaction requirements. However, if you need more advanced features like annotations, document editing, real-time collaboration, or enterprise-level support, Nutrient stands out as a powerful solution.

In this tutorial, we explored how to build a PDF viewer with both PDF.js and Nutrient. While PDF.js is suitable for smaller projects or those with basic needs, Nutrient’s advanced features and enterprise support make it the ideal choice for larger projects where enhanced functionality and performance are essential.

We’ve also created similar tutorials for various web frameworks and libraries:

To get started with our jQuery PDF viewer, try it for free, or launch our web demo.

FAQ

Here are a few frequently asked questions about PDF.js and Nutrient.

What is the main difference between PDF.js and Nutrient?

PDF.js is an open source JavaScript library focused on rendering PDFs in the browser, while Nutrient offers a commercial PDF SDK with more features like annotations, form filling, and OCR.

Can I add annotations to PDFs using both PDF.js and Nutrient?

Yes, both libraries support annotations, but Nutrient provides a more extensive set of tools and customization options for annotations.

Is it possible to handle large PDFs with both PDF.js and Nutrient?

Yes, both libraries can handle large PDFs, but Nutrient has optimizations for better performance with larger files and complex documents.

How customizable are the PDF.js and Nutrient viewers?

Both viewers can be customized, but Nutrient offers more flexibility for custom UI elements and tools, especially in enterprise applications.

Can I use Nutrient and PDF.js in frameworks like Angular or React?

Yes, both libraries are compatible with popular JavaScript frameworks, though integration approaches may vary depending on project requirements.

Author
Hulya Masharipov Technical Writer

Hulya is a frontend web developer and technical writer at Nutrient who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

Related products
Share post
Free trial Ready to get started?
Free trial