Generate PDFs from a Template Using JavaScript

With PSPDFKit, you can generate PDF documents entirely on the client side in a browser by merging predefined PDF templates with data. This is useful for generating structured documents, such as invoices, packing slips, tickets, checklists, contracts, or labels.

Loading the PDF Template

This guide uses the Getting Started on Web > Integrating into a Vanilla JavaScript Project as a basis for generating PDFs. Please make sure to follow the steps in that guide to set up your project.

First, you’ll need to load the PDF document you wish to populate with the data. Depending on your use case, the document can be loaded in a PDF viewer where a user interface is presented to the user, or headlessly, without a UI.

Load the document in the PDF viewer:

import "./assets/pspdfkit.js";

const baseUrl = `${window.location.protocol}//${}/assets/`;

(async () => {
  const instance = await PSPDFKit.load({
    document: "/document.pdf", // Path to the PDF document.
    container: "#pspdfkit"

Load in headless mode so that no UI is presented to the user:

import "./assets/pspdfkit.js";

const baseUrl = `${window.location.protocol}//${}/assets/`;

(async () => {
  const instance = await PSPDFKit.load({
    document: "/document.pdf", // Path to the PDF document.
    headless: true

Populating the Document with Data

To populate the document, you can overlay annotations onto the PDF template. In the following example, you’ll take a JSON data set and overlay TextAnnotations at predefined coordinates:

// data.json

  "company": "PSPDFKit",
  "companyInfo": "PSPDFKit",
  "companyName": "PSPDFKit"

If you look at this guide’s example PDF template, you can see placeholders for COMPANY, [Company Info], and Company Ltd.. To detect the position of these placeholders, you can use the search API:

const searchQuery = "Company Ltd."; // The text to search for.

const bbox = (await

This will show the current position of the text in the document.

Now, create the annotations with the text you want to overlay. For the text property, bring in the data from the JSON file:

// index.js

// Create a free text annotation.
const textAnnotation = new PSPDFKit.Annotations.TextAnnotation({
  boundingBox: bbox, // Set the bounding box of the text annotation.
  fontSize: 8,
  text: {
    format: "plain",
    value: data.companyName
  }, // The text to overlay.
  pageIndex: 0, // The page index to overlay the text on.
  fontColor: PSPDFKit.Color.RED,
  backgroundColor: PSPDFKit.Color.WHITE

// Add the annotations to the document.
await instance.create(textAnnotation);

The backgroundColor property is set to white. This will help create an opaque text annotation to hide the page text underneath.

You can also change the fontSize property; just be aware that you need to adapt the boundingBox size proportionally to the new font size.

The data from the JSON file will now appear in the PDF template as text annotations.

See our guide on programmatically creating annotations for more details.

It’s also possible to overlay other annotation types, such as signatures, images, and stamps.

Flattening Annotations

Once the annotations have been overlaid on top of the PDF template, they can optionally be flattened to prevent modification:

await instance.exportPDF({ flatten: true });

See our flatten annotations guide for more details.

After flattening the annotations, the PDF template will look like what’s shown below.

End result


You can find the example PDF template and the code on GitHub.

The example demo first loads the document in headless mode and then loads with the UI. This allows you to process the PDF before sending it over the network, or you can use it for light processing, like adding watermarks to the document.

Serving the Example Project

You can serve the PDF template to the user in a browser. For this, use the serve HTTP package.

  1. Install the serve package:

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

serve -l 8080 .
  1. Navigate to http://localhost:8080 to view the website.

Using Annotations

Another way to do this is via annotations instead of using page text placeholders. (The above example tried to find the position of placeholders and updated it with dynamic data.) Use text annotations with predefined visuals (font, size, color, etc.) but with a replacement string. Here, you’ll retrieve all annotations on the page, iterate through all text annotations, and update them if they have the placeholder text.

For example, you could have the following text annotation as part of the template.

annotation example

Then, you can perform the token replacement via the following:

const replacements = {
  "[[Company]]": "PSPDFKit"

const annotationsToUpdate =
  // Retrieve annotations for a page.
  (await instance.getAnnotations(0))
    // Filter to text annotations.
    .filter((it) => it instanceof PSPDFKit.Annotations.TextAnnotation)
    // Replace annotations with text that matches any replacement token.
    .map((annotation) =>
        ? annotation.set("text", {
            format: annotation.text.format, 
				value: replacements[annotation.text.value]
        : annotation

// Annotations are immutable. `annotationsToUpdate` contains new instances of annotations. You need to use `instance.update` to actually update them.
await instance.update(annotationsToUpdate);

This will replace the [Company] annotation with PSPDFKit.

annotation result

Saving the PDF Document

See our guide for saving a document.

You can also check out this blog post to learn more.