Integrate into a PWA Project
In this guide, you’ll configure your PSPDFKit project for deployment as a progressive web app (PWA).
It will tick the majority of the boxes of the baseline PWA checklist. We’ve already built a more advanced implementation of this example, which you can try live on our website.
If you’ve never heard about PWAs and want to find out more about them, we highly recommend you check out the dedicated site from Google. You can also read our blog post on PWAs.
Prerequisites
This guide assumes you’ve already configured PSPDFKit for your specific framework. If you haven’t, select your framework from the list above and configure PSPDFKit. Then return to this guide.
Our PWA must be served by a web server over HTTPS, so please make sure your server is configured to serve pages in HTTPS. In development, however, we don’t need HTTPS since browsers treat localhost
as a secure origin.
A Note about Progressive Enhancement
By definition, PWAs are progressive, meaning they’re inclusive and they rely heavily on progressive enhancement. When building a PWA, it’s good to always keep this in mind and provide a basic experience for every user of the application.
Richer features should be built on top of an always-working barebones implementation. We highly recommend using feature detection to provide progressive enhancement so that applications won’t break in older browsers that don’t support a specific feature.
Opening a PDF File
To read the PDF, you’ll need a helper to read the selected file from disk using the FileReader API:
function registerFilePicker(element, callback) { ... }
/* ./src/app.js */
function registerFilePicker(element, callback) {
function handler(event) {
if (event.target.files.length == 0) {
event.target.value = null;
return;
}
var pdfFile = event.target.files[0];
if (pdfFile.type !== "application/pdf") {
alert("Invalid file type, please load a PDF.");
return;
}
var reader = new FileReader();
reader.addEventListener("load", function(event) {
var pdf = event.target.result;
callback(pdf, pdfFile);
});
reader.addEventListener("error", function(error) {
alert(error.message);
});
reader.readAsArrayBuffer(pdfFile);
event.target.value = null;
}
element.addEventListener("change", handler);
return function() {
element.removeEventListener("change", handler);
};
}
callback
is a function that gets the pdf
in the ArrayBuffer
format so that you can load it directly with PSPDFKit. It also gets the selected File
object.
Once you have this helper, you can add the code to initialize PSPDFKit for Web:
./src/app.js
var pspdfkitInstance = null;
var filePicker = document.querySelector('input[type="file"]');
registerFilePicker(filePicker, function(pdf, fileInfo) {
if (pspdfkitInstance) {
PSPDFKit.unload(pspdfkitInstance);
}
PSPDFKit.load({
document: pdf,
container: ".PSPDFKit-container",
// See https://pspdfkit.com/api/web/PSPDFKit.Configuration.html#enableServiceWorkerSupport
enableServiceWorkerSupport: true
}).then(function(instance) {
pspdfkitInstance = instance;
});
});
Our advanced PWA example allows us to load PDF files from a remote server, and it uses IndexedDB to cache them locally for offline use. It also uses the History API to easily load files via URL.
Adding Caching and Offline Capabilities with Service Workers
One of the most important features of PWAs is the ability to load fast and to work on slow network conditions, or even offline. To achieve this, you can use a service worker to cache the application shell and, when available, use the network only to fetch necessary data.
The service worker is a script you register in the application shell. It runs in the background, separate from a webpage. It can intercept and handle network requests, allowing you to cache responses programmatically.
In case you’re not familiar with service workers, we highly recommend you read this excellent introductory blog post.
Now, create the ./serviceWorker.js
file:
/* ./serviceWorker.js */
console.log("Hello from the Service Worker");
Then, register it in your application shell:
<!-- ./src/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>PSPDFKit PWA</title>
+ <script>
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', function () {
+ navigator.serviceWorker.register('./serviceWorker.js')
+ })
+ }
+ </script>
This uses feature detection to determine whether service workers are supported and to register your serviceWorker.js
when the feature is available.
You can now start your application and try it out in a web browser. In the Application tab of Chrome Dev Tools, you’ll see that your service worker has been registered and is active!

For local development, it’s a good idea to check the Update on reload option so that the service worker is updated every time the page reloads. You can clear the service worker storage any time from this panel using the Clear storage view.
A Note about the Service Worker API
The Service Worker API is low level, flexible, and powerful. Because of this, it usually requires some boilerplate code to do common tasks like activate the service worker, intercept requests and cache responses, clear the cache, and precache files.
To simplify those tasks, Google developed Workbox, an open source library that abstracts away all the complexity and makes building PWAs easy. Before deploying to production, we recommend using Workbox to manage your service workers.
Workbox can help with doing more than precaching, and it allows you to configure how each resource should be cached and how routes should be handled.
Complex applications will likely need to use the network to fetch data before they can render content in the app shell. In those cases, it’s important to choose the correct caching strategy for your data.
Please refer to the Workbox website to learn more about how to handle advanced use cases.
Final Touches
Now that your app has offline capabilities, you only need to add a web app manifest to make the application recognizable by the web browser and to describe how the app should behave when installed on users’ devices.
The web app manifest is a file whose name is manifest.json
. It contains metadata like the name of the app, the paths to icons and their sizes, the start URL, and the theme color. Now you’ll create a basic one for your PWA:
touch ./src/manifest.json
{
"name": "PSPDFKit for Web PWA",
"short_name": "PSPDFKit",
"icons": [
{
"src": "images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"background_color": "#0089AA",
"theme_color": "#0089AA"
}
Finally, you need to register the manifest in the app pages — in your case, index.html
:
<!-- ./src/index.html -->
<title>PSPDFKit PWA</title>
<script>
if ("serviceWorker" in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('./serviceWorker.js')
})
}
</script>
+<link rel="manifest" href="./manifest.json">
You can verify the manifest in the Application tab of Chrome Dev Tools:

The web app manifest also makes it possible to display an App Install Banner or an Add to Home Screen dialog. You can follow the guidelines from Google to learn how to create and display one.
Limitations
Disk Quota
Web browsers define quotas either per origin (Chrome and Opera) or per API (e.g. IndexedDB, service workers). When storing files via web APIs, it’s a good idea to keep this in mind and ideally monitor the disk quota status to avoid failures. Apps can check how much quota they’re using with the Quota Management API. We highly recommend you check out this research report on browser storage from HTML5 Rocks.
Precached PSPDFKit Assets
In this guide, you saw how to precache all the PSPDFKit for Web assets, including the JavaScript fallback pspdfkit.asm.js
. However, when the target browser supports WebAssembly, it’d be better to exclude this file from the precache manifest and vice versa: When the target browser doesn’t support WebAssembly, you shouldn’t precache the WASM module pspdfkit.wasm
. For production applications, we recommend generating separate manifests and service workers and serving them conditionally.
Conclusion
Hopefully the above information demonstrated how easy it is to integrate PSPDFKit for Web and make a simple PWA to display PDF documents. This is possible thanks to Workbox, which provides a simple yet powerful abstraction on top of the Service Worker API.
The PWA built in this guide meets all the requirements of a baseline PWA, however, it’s just a proof of concept that shouldn’t be used in production.
We also built a more comprehensive and advanced example where PDF documents can be fetched from a remote server and are stored locally in IndexedDB for offline use. The advanced example also uses the History API to provide a unique working URL for every PDF document, along with a connectivity status indicator.
If you want to learn more about progressive web apps, we highly encourage you to check out the following resources: