Complete guide to PDF.js

Table of contents

    Learn how to set up PDF.js for browser-based PDF rendering, add navigation and zoom controls, and use the annotation editor. For advanced editing features, see Nutrient Web SDK.
    Complete guide to PDF.js
    TL;DR

    This guide covers PDF.js setup, rendering, navigation, and the annotation editor. For advanced features like reliable annotation saving, digital signatures, or real-time collaboration, see Nutrient Web SDK.

    What is PDF.js?

    PDF.js(opens in a new tab) is Mozilla’s open source JavaScript library for rendering PDFs in browsers without plugins. It uses the HTML5 Canvas API to draw pages client-side. The latest version is v5.4.449 (November 2025).

    PDF.js supports rendering, text search, thumbnails, zoom, rotation, and displaying annotations. Recent versions added a limited annotation editor (free text, highlight, stamp, ink), though these use HTML overlays and may not embed properly into a PDF.

    If you’re working on a simple project or a website that requires basic PDF viewing, PDF.js may be all you need. However, if your business is dealing with large-scale documents or requires advanced PDF manipulation, Nutrient Web SDK offers the tools and capabilities to take your PDF workflows to the next level.

    How PDF.js works: Core concepts and architecture

    PDF.js uses a modular architecture with three layers. You can include only the layers you need, reducing bundle size. Here’s what each layer does:

    1. Core layer — This is the lowest-level layer of PDF.js, responsible for parsing the binary format of a PDF file and converting it into an internal representation that can be used by higher-level layers. The core layer is typically used directly only by advanced users who need fine-grained control of the parsing process.
    2. Display layer — The display layer builds upon the core layer and provides a more user-friendly API for rendering PDF files. With the display layer, you can easily render a PDF page into a <canvas> element using just a few lines of JavaScript code. This layer is suitable for most day-to-day use cases.
    3. Viewer layer — The viewer layer is a ready-to-use user interface (UI) that comes with PDF.js. It includes features like search, rotation, a thumbnail sidebar, and more. The viewer layer is built on top of the display layer and provides a complete PDF viewing experience out of the box.

    PDF.js key features

    PDF.js provides features for viewing and basic annotation of PDF documents:

    • Render PDF documents in the browser using the HTML5 <canvas> element
    • Search for text within a document
    • View page thumbnails
    • Zoom in and out of pages
    • Rotate pages
    • Display existing annotations (text highlights, comments, links)
    • Create basic annotations (free text, highlight, stamp, ink) via the annotation editor
    • Display and fill basic AcroForm fields (limited support; no XFA)
    • View and navigate bookmarks and document outlines
    • Print PDF documents
    • Improved accessibility with alt text support for images

    Recent PDF.js updates

    Version 5.4.449 (November 2025) includes:

    • PDF merging — Create PDFs from multiple source documents with merged structure trees
    • Annotation editor shortcuts — Save edited comments with Control-Enter
    • Improved search — Better punctuation handling and regex normalization
    • Memory optimization — Reduced memory usage for thumbnails
    • Tagged PDF telemetry — Better tracking for accessibility features

    Check the PDF.js releases page(opens in a new tab) for the full changelog.

    Limitations of PDF.js

    PDF.js is designed primarily for viewing. Here are specific limitations:

    • Performance — Client-side rendering slows down with large or complex PDFs. Single-threaded processing can block the UI.
    • Text selection — Incorrect bounding boxes cause spacing issues in text selection, especially in documents with complex layouts.
    • Limited annotation editing — The annotation editor supports only four types (free text, highlight, stamp, ink). Annotations are rendered as HTML overlays and may not save properly into the PDF binary for compatibility with other PDF readers. There’s no support for shapes, arrows, notes, or measurement tools.
    • Limited form support — Basic AcroForm field display only. No XFA forms, no programmatic form filling, no form creation.
    • No signatures — No support for digital or electronic signatures.
    • No redaction — Cannot permanently remove sensitive content from documents.
    • No Office file support — PDF only. Cannot render Word, Excel, or PowerPoint files.
    • No real-time collaboration — No built-in support for multiple users annotating simultaneously.
    • No document comparison — Cannot compare two PDF versions side by side.
    • Accessibility gaps — Limited WCAG compliance for assistive technologies.

    Why teams choose Nutrient over PDF.js

    PDF.js displays PDFs. When your application needs editing, you’ll write custom code or find workarounds. Nutrient handles the editing side.

    Annotations that save

    PDF.js annotations render as HTML overlays and often don’t persist. Nutrient supports 17+ annotation types (highlights, shapes, arrows, stamps, sticky notes, measurement tools) that write directly to the PDF file. Other PDF readers can open the file and see the annotations.

    Forms and signatures

    Nutrient includes form filling with text fields, checkboxes, dropdowns, and validation. For signatures, you can capture handwritten input or add cryptographic signatures (eIDAS, HIPAA, GDPR compliant).

    Office files and scanned documents

    Nutrient renders Word, Excel, and PowerPoint directly. OCR extracts text from scanned PDFs. Hybrid rendering (client + server) keeps large documents responsive.

    Redaction and compliance

    Redaction permanently deletes content from PDFs; it doesn’t just hide it. Document comparison shows pixel-level differences between two versions. WCAG 2.2 AA accessibility is built in. Audit trails track views, annotations, and signatures.

    AI Assistant

    Query document content, generate summaries, classify documents, or extract specific data.

    Real-time collaboration

    Multiple users annotate the same document simultaneously. Changes sync instantly.

    Support

    Nutrient includes technical support with SLAs. PDF.js has community support only.

    Test it:

    When to use PDF.js vs Nutrient Web SDK

    FeaturePDF.jsNutrient Web SDK
    View PDFs
    Text search
    Display annotations
    Create/edit annotationsLimited (4 types)✅ (17+ types)
    Save annotations to PDFLimited
    Form fillingLimited
    Digital signatures
    Redaction
    Office file support
    Real-time collaboration
    Document comparison
    AI assistant
    OCR
    WCAG 2.2 AALimited
    Dedicated support❌ (community only)

    Use PDF.js if you only need to display PDFs and don’t require users to save changes.

    Use Nutrient if users need to annotate, sign, fill forms, or collaborate — and expect their work to save reliably.

    Next steps

    While PDF.js is a great open source tool for basic PDF viewing, Nutrient Web SDK is the premium solution for businesses needing advanced features and performance. To try it, start with the demo or request a consultation.

    For a detailed comparison, see our PDF.js vs. Nutrient guide.

    The following sections cover PDF.js setup and usage for basic viewing.

    Getting started with PDF.js

    1. Download or clone PDF.js — You can download(opens in a new tab) the library as a ZIP file or clone the repository using Git.
    2. Prepare the files — Extract the ZIP file and copy the pdf.mjs and pdf.worker.mjs files from the build/ folder to your project directory.
    3. Include PDF.js in your HTML — Add the following script tag to your HTML file to load the PDF.js library:
    <script src="./pdf.mjs" type="module"></script>

    After including the script tag, you can start using PDF.js to render PDF files on your webpage.

    Rendering a PDF file with PDF.js

    To render a PDF file using PDF.js, follow these steps.

    1. Add a <canvas>(opens in a new tab) element to your HTML where the PDF will be displayed:

      <canvas id="pdf-canvas"></canvas>
    2. Create a file named index.js.

      Next, use document.getElementById to select the <canvas> element in your HTML where the PDF will be displayed:

      const canvas = document.getElementById("pdf-canvas");

      Define the URL of the PDF file you want to render. Ensure this file is accessible from your project directory:

      const pdfUrl = "document.pdf";

      Set the path to the PDF.js worker file to enable PDF rendering. Use pdfjsLib.getDocument(pdfUrl).promise to load the PDF file, which returns a Promise resolving to a PDFDocumentProxy object:

      pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.mjs";
      pdfjsLib.getDocument(pdfUrl).promise.then(function (pdfDoc) {
      // Continue with further steps.
      });

      Retrieve the desired page from the loaded PDF document using pdfDoc.getPage(1). This returns a Promise resolving to a PDFPageProxy object representing the page:

      pdfDoc.getPage(1).then(function (page) {
      // Continue with further steps.
      });

      Calculate and set the dimensions of the canvas to match the size of the PDF page to ensure correct display:

      const viewport = page.getViewport({ scale: 1 });
      canvas.width = viewport.width;
      canvas.height = viewport.height;

      Get the 2D rendering context from the canvas. Create a renderContext object with the canvas context and viewport settings. Call the page.render() method to render the PDF page onto the canvas:

      const ctx = canvas.getContext("2d");
      const renderContext = {
      canvasContext: ctx,
      viewport: viewport,
      };
      page.render(renderContext);

      Implement error handling to manage cases where the PDF file might be missing or corrupted. Use the catch() method to log errors to the console or display an appropriate message:

      pdfjsLib
      .getDocument(pdfUrl)
      .promise.then(function (pdfDoc) {
      // Handling and rendering logic.
      })
      .catch(function (error) {
      console.log("Error loading PDF file:", error);
      });
    3. Include the index.js file in your HTML file:

    <script src="./index.js" type="module"></script>

    The type="module" attribute allows the use of ES6 features like import/export and ensures that the script is loaded asynchronously, avoiding global scope issues.

    Make sure to add your PDF file to the same directory as the HTML file. You can use the demo PDF file as an example.

    This is a simple example of how to render a PDF file using PDF.js. PDF.js provides many more options and features that you’ll explore in the next sections.

    Running the project

    To run the project, follow these steps.

    1. Install the serve package:

      Terminal window
      npm install --global serve
    2. Serve the current directory:

      Terminal window
      serve -l 8080 .
    3. Visit http://localhost:8080 to view the project.

    pdfjs demo

    User interface enhancements

    To improve the user experience when working with PDFs, you can add various UI enhancements such as navigation controls and zoom functionalities. The next sections explain how you can integrate these features.

    Adding navigation controls

    1. Include buttons for navigation in your HTML file:

      <button id="prev-page">Previous Page</button>
      <button id="next-page">Next Page</button>
    2. Update your index.js file to handle page navigation. Add event listeners to the buttons and manage page navigation:

    index.js
    const initialState = {
    pdfDoc: null,
    currentPage: 1,
    zoom: 1,
    };
    document.getElementById("prev-page").addEventListener("click", function () {
    4 collapsed lines
    if (initialState.pdfDoc && initialState.currentPage > 1) {
    initialState.currentPage--;
    renderPage(initialState.currentPage);
    }
    });
    document.getElementById("next-page").addEventListener("click", function () {
    7 collapsed lines
    if (
    initialState.pdfDoc &&
    initialState.currentPage < initialState.pdfDoc.numPages
    ) {
    initialState.currentPage++;
    renderPage(initialState.currentPage);
    }
    });
    function renderPage(pageNumber) {
    if (!initialState.pdfDoc) return;
    initialState.pdfDoc
    .getPage(pageNumber)
    .then((page) => {
    const viewport = page.getViewport({
    scale: initialState.zoom,
    });
    canvas.width = viewport.width;
    canvas.height = viewport.height;
    const ctx = canvas.getContext("2d");
    const renderContext = {
    canvasContext: ctx,
    viewport: viewport,
    };
    page
    .render(renderContext)
    .promise.then(() => {
    console.log("Rendering complete");
    })
    .catch((error) => {
    console.log("Error rendering page:", error);
    });
    })
    .catch((error) => {
    console.log("Error loading page:", error);
    });
    }

    Adding zoom controls

    1. Include buttons for zoom controls in your HTML file:

      <button id="zoom-in">Zoom In</button> <button id="zoom-out">Zoom Out</button>
    2. Update index.js to manage zoom functionality. Add event listeners to the zoom buttons and adjust the scale of the viewport:

    document.getElementById("zoom-in").addEventListener("click", function () {
    if (initialState.pdfDoc) {
    initialState.zoom *= 4 / 3; // Increase zoom scale.
    renderPage();
    }
    });
    document.getElementById("zoom-out").addEventListener("click", function () {
    if (initialState.pdfDoc) {
    initialState.zoom *= 2 / 3; // Decrease zoom scale.
    renderPage();
    }
    });
    0:00
    0:00

    Advanced features and customizations

    This section explores more advanced features and customizations you can add to your PDF viewer. These include handling annotations, enabling text selection, controlling rendering options, navigating documents, and manipulating PDFs. Each feature enhances the functionality and user experience of your PDF viewer, making it more interactive and efficient.

    Handling PDF annotations

    PDF.js provides two ways to work with annotations:

    1. Reading existing annotations — Use the getAnnotations() method to access annotations like links, highlights, and comments already in the PDF.
    2. Annotation editor — The built-in annotation editor supports free text, highlight, stamp, and ink annotations. Access it via the toolbar in the default viewer.

    The annotation editor has limitations: It renders annotations as HTML overlays rather than embedding them directly into the PDF structure. This means annotations created in PDF.js may not display correctly in other PDF readers like Adobe Acrobat.

    Here’s an example of how to read and render existing PDF annotations:

    // Load annotation data.
    page
    .getAnnotations()
    .then(function (annotations) {
    annotations.forEach(function (annotation) {
    if (annotation.subtype === "Text") {
    // Render a text annotation.
    const textRect = annotation.rect;
    const text = document.createElement("div");
    text.style.position = "absolute";
    text.style.left = textRect[0] + "px";
    text.style.top = textRect[1] + "px";
    text.style.width = textRect[2] - textRect[0] + "px";
    text.style.height = textRect[3] - textRect[1] + "px";
    text.style.backgroundColor = "green";
    text.style.opacity = "0.5";
    text.innerText = annotation.contents;
    canvas.parentNode.appendChild(text);
    } else if (annotation.subtype === "Highlight") {
    // Render a highlight annotation.
    const highlightRect = annotation.rect;
    const highlight = document.createElement("div");
    highlight.style.position = "absolute";
    highlight.style.left = highlightRect[0] + "px";
    highlight.style.top = highlightRect[1] + "px";
    highlight.style.width = highlightRect[2] - highlightRect[0] + "px";
    highlight.style.height = highlightRect[3] - highlightRect[1] + "px";
    highlight.style.backgroundColor = "yellow";
    highlight.style.opacity = "0.5";
    canvas.parentNode.appendChild(highlight);
    }
    });
    })
    .catch(function (error) {
    console.log("Error loading annotations:", error);
    });

    Make sure the PDF file you’re using actually contains annotations.

    This code snippet retrieves all the annotations for the current page using the page.getAnnotations() method. It then loops through each annotation and checks its subtype to determine what type of annotation it is.

    For text annotations, it creates a div element, sets its position and dimensions using the annotation’s rectangle coordinates, and adds it to the container element with a green background color and opacity of 0.5. Similarly, for highlight annotations, it creates a div element, sets its position and dimensions using the annotation’s rectangle coordinates, and adds it to the container element with a yellow background color and opacity of 0.5.

    This code will render text annotations as green semitransparent rectangles with the text contents of the annotation on top of the PDF page at the position specified by the annotation’s rectangle coordinates, and it’ll render highlight annotations as yellow rectangles with 50 percent opacity.

    Check out the source code for this example on GitHub(opens in a new tab).

    Handling PDF text selection

    PDF.js provides support for selecting and copying text from PDF files. PDF text can be accessed using the getTextContent() method on a PDF page.

    Here’s an example of how to extract text from a PDF page:

    page.getTextContent().then(function (textContent) {
    let text = "";
    for (let i = 0; i < textContent.items.length; i++) {
    const item = textContent.items[i];
    text += item.str;
    }
    console.log(text);
    });

    This code will extract all the text from the PDF page and log it to the console.

    Controlling PDF rendering

    PDF.js provides many options for controlling how PDF files are rendered. These options can be passed as parameters to the page.render() method.

    Here are some of the most common options:

    • canvasContext — Specifies the rendering context to use for rendering the PDF page. This is typically a 2D canvas context.
    • viewport — Specifies the viewport to use for rendering the PDF page. The viewport defines the part of the PDF page that should be displayed on the canvas. It can be customized with options such as scale, rotation, and offset.
    • background — Specifies the color or pattern to use for the background of the canvas. This can be set to a CSS color value or a canvas pattern object.

    Here’s an example of how to use some of these options:

    page.render({
    canvasContext: ctx,
    viewport: page.getViewport({ scale: 1.5 }),
    background: "rgb(255,0, 0)",
    });

    This will render the PDF page with a 1.5x zoom and a red background.

    PDF.js provides several methods for navigating PDF documents, including scrolling, zooming, and searching. Here’s an overview of some of the most commonly used navigation methods:

    • Scrolling — PDF.js enables you to scroll through a document using the mouse or touchpad. You can also use the scrollbar to navigate through the document.
    • Zooming — You can zoom in and out of a PDF document using the mouse or touchpad. You can also use the zoom buttons on the toolbar to zoom in and out.
    • Searching — PDF.js provides a search bar that enables you to search for specific words or phrases in a PDF document. You can also use the “find” command to search for text within the document.

    Manipulating a PDF document with PDF.js

    While PDF.js is primarily a PDF viewer, it offers some limited manipulation capabilities:

    • Users can fill in form fields displayed in the PDF.
    • The annotation editor allows adding basic markup (free text, highlights, stamps, ink drawings).

    Note that saving changes back to the PDF file requires additional implementation. PDF.js doesn’t provide built-in save functionality — you need to handle serialization yourself or use a server-side solution.

    For full PDF editing capabilities, consider Nutrient Web SDK.

    Troubleshooting

    Here are some common issues you might encounter, along with their fixes:

    • Check the console — Most PDF.js errors show up in the browser console.
    • Validate the PDF — Corrupted files fail silently. Test with a different PDF that opens correctly elsewhere.
    • Update PDF.js — Older versions have bugs that are fixed in newer releases.
    • Check browser support — PDF.js requires modern JavaScript features. Test in current versions of Chrome, Firefox, or Safari.
    • Review worker path — If nothing renders, workerSrc might point to the wrong location.

    Best practices

    • Use the latest version — Bug fixes and performance improvements ship regularly.
    • Optimize PDF files — Smaller files render faster. Compress images and remove unused objects.
    • Cache PDFs — Store rendered pages to avoid rerendering on repeat views.
    • Use a CDN — Serve PDF files from edge locations closer to users.
    • Profile rendering — Large or complex PDFs can block the main thread. Use browser developer tools to identify bottlenecks.

    Conclusion

    PDF.js is a solid choice for basic PDF viewing. For applications that need editing capabilities, try the Nutrient demo or contact Sales.

    FAQ

    What is Nutrient Web SDK?

    Nutrient Web SDK is a commercial JavaScript library for viewing and editing PDFs in the browser. It provides 17+ annotation types, form filling, digital signatures, redaction, Office file conversion, OCR, and real-time collaboration. It includes dedicated technical support with SLAs.

    What does Nutrient Web SDK have that PDF.js doesn’t?
    • 17+ annotation types that save to the PDF (PDF.js has 4 types, saves unreliably)
    • Form filling and creation
    • Digital and electronic signatures
    • Permanent redaction
    • Office file rendering (Word, Excel, PowerPoint)
    • Real-time collaboration
    • Document comparison
    • An AI assistant
    • OCR
    • WCAG 2.2 AA accessibility
    Why is Nutrient Web SDK faster with large PDFs?

    Hybrid rendering splits work between the client and the server. PDF.js renders everything client-side, which blocks the main thread on large files.

    Does Nutrient Web SDK support real-time collaboration?

    Yes. Multiple users can view and annotate the same document simultaneously. Changes sync instantly across all connected clients. This requires integration with Nutrient’s Document Engine or Instant syncing component.

    What file formats does Nutrient Web SDK support?

    Nutrient renders PDFs, images (JPEG, PNG, TIFF), and Office documents (Word, Excel, PowerPoint) directly in the browser. PDF.js only supports PDF files.

    How do I get started with Nutrient Web SDK?

    Sign up for a free trial to test the SDK in your project. The documentation includes getting started guides for React, Angular, Vue.js, and vanilla JavaScript. For questions, contact Support or Sales.

    Hulya Masharipov

    Hulya Masharipov

    Technical Writer

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

    Explore related topics

    Try for free Ready to get started?