---
title: "Advanced PDF.js loading: Streaming, range requests, and web worker patterns"
canonical_url: "https://www.nutrient.io/blog/pdfjs-advanced-loading-streaming-workers/"
md_url: "https://www.nutrient.io/blog/pdfjs-advanced-loading-streaming-workers.md"
last_updated: "2026-07-31T20:03:39.940Z"
description: "Advanced PDF.js loading strategies: progressive display, range requests, custom transports, worker configuration, render cancellation, and `OffscreenCanvas`."
---

**TL;DR**

- Stream large PDFs progressively from a URL and tune `rangeChunkSize`/`disableAutoFetch` for slow networks.

- Use `PDFDataRangeTransport` when bytes come from a custom source (encrypted storage, WebSocket, signed API).

- Cancel pending `page.render()` calls during fast scrolling and use `OffscreenCanvas` to render off the main thread.

## Prerequisites

This guide assumes you’ve already wired up a PDF.js viewer — `getDocument`, a worker URL on `GlobalWorkerOptions.workerSrc`, and a `PDFDocumentProxy` you can render pages from. If you haven’t, start with our blog on [how to set up a custom PDF.js viewer in React](https://www.nutrient.io/blog/pdfjs-react-viewer-setup.md).

You’ll also need:

- `pdfjs-dist` 4.x or later

- A server that supports HTTP `Range` headers (for the range-request and progressive-loading sections)

## Streaming/progressive loading

PDF.js can display a document before it has fully downloaded, either by letting the browser issue range requests automatically or by supplying a custom transport for full control over how bytes arrive.

### Progressive loading from a URL

`getDocument` doesn’t accept a `ReadableStream` directly — its `data` option only takes `TypedArray | ArrayBuffer | string`. To stream a PDF as it downloads, pass the URL and let PDF.js handle range requests automatically (see below), or supply a custom `PDFDataRangeTransport` for full control over how bytes are fetched.

```tsx

import { getDocument } from "pdfjs-dist";

const loadingTask = getDocument({
  url: "https://example.com/large-document.pdf",
});

const pdfDocument = await loadingTask.promise;
// Pages become available as data arrives.

```

### Range requests

PDF.js can request specific byte ranges of a PDF file, enabling fast initial page display, even for huge documents. This requires the server to support `Range` headers:

```tsx

const loadingTask = getDocument({
  url: "https://example.com/large-document.pdf",
  rangeChunkSize: 1024 * 1024, // 1MB chunks
  disableAutoFetch: false,      // true = only fetch on demand
  disableStream: false,         // true = disable streaming
});

```

### Configuration options

| Option             | Default      | Description                                                 |
| ------------------ | ------------ | ----------------------------------------------------------- |
| `rangeChunkSize`   | 65536 (64KB) | Size of each range request chunk                            |
| `disableAutoFetch` | `false`      | If `true`, only fetches data when pages are actually needed |
| `disableStream`    | `false`      | If `true`, disables streaming (waits for full download)     |
| `disableRange`     | `false`      | If `true`, disables range requests entirely                 |

**`disableAutoFetch` has a dependency:** It only takes effect when `disableStream` is `false`. If you turn off streaming, the rest of the file downloads regardless of `disableAutoFetch`.

### Custom transport with PDFDataRangeTransport

Use a custom `PDFDataRangeTransport` for complete control over how PDF data is fetched (e.g. from a custom API, encrypted storage, or WebSocket):

```tsx

import { getDocument, PDFDataRangeTransport } from "pdfjs-dist";

class CustomTransport extends PDFDataRangeTransport {
  constructor(length, initialData) {
    super(length, initialData);
  }

  requestDataRange(begin, end) {
    // Fetch the byte range from your custom source.
    fetchBytesFromCustomSource(begin, end).then((data) => {
      this.onDataRange(begin, data);
    });
  }
}

// Usage.
const transport = new CustomTransport(fileSize, headerBytes);
const loadingTask = getDocument({ range: transport });

```

## Worker configuration

PDF.js always parses in a background worker; configure how that worker’s script loads, swap out its communication channel, or turn it off entirely for debugging.

### Standard worker setup

Point `GlobalWorkerOptions.workerSrc` at the worker script, either as a plain URL string or resolved from `node_modules`:

```tsx

import { GlobalWorkerOptions } from "pdfjs-dist";

// Option 1: URL string.
GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs";

// Option 2: Resolved from node_modules.
GlobalWorkerOptions.workerSrc = new URL(
  "pdfjs-dist/legacy/build/pdf.worker.min.mjs",
  import.meta.url,
).toString();

```

### Custom worker port (MessagePort)

You can pass any `MessagePort` as the worker communication channel:

```tsx

const { port1, port2 } = new MessageChannel();

// Send port2 to an existing worker.
existingWorker.postMessage({ pdfWorkerPort: port2 }, [port2]);

// Use port1 with PDF.js.
const loadingTask = getDocument({
  url: "document.pdf",
  workerPort: port1,
});

```

### Disabling workers (debugging)

For debugging, you can run PDF.js in the main thread by passing `disableWorker: true` to `getDocument`:

```tsx

const loadingTask = getDocument({
  url: "document.pdf",
  disableWorker: true,
});

```

Only use this for debugging — it blocks the main thread during parsing.

## Page render cancellation

For fast-scrolling scenarios, cancel pending renders to free resources:

```tsx

let activeRenderTasks = new Map();

async function renderPage(page, canvas, viewport) {
  const pageNumber = page.pageNumber;

  // Cancel any existing render for this page.
  if (activeRenderTasks.has(pageNumber)) {
    activeRenderTasks.get(pageNumber).cancel();
  }

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

  activeRenderTasks.set(pageNumber, renderTask);

  try {
    await renderTask.promise;
    activeRenderTasks.delete(pageNumber);
  } catch (error) {
    if (error.name === "RenderingCancelledException") {
      // Expected when canceled — do nothing.
      return;
    }
    throw error;
  }
}

```

### RenderTask API

`page.render()` returns a `RenderTask` with a promise, a `cancel()` method, and an `onContinue` hook for pausing and resuming rendering:

```tsx

const renderTask = page.render({ canvasContext, viewport });

renderTask.promise  // Promise that resolves when rendering completes.
renderTask.cancel() // Cancel the rendering.
renderTask.onContinue = (cont) => {
  // Called during rendering — allows you to pause/resume.
  cont(); // Call to continue rendering.
};

```

## Custom canvas factory

Override how canvases are created for rendering:

```tsx

const loadingTask = getDocument({
  url: "document.pdf",
  canvasFactory: {
    create(width, height) {
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      return canvas;
    },
    reset(canvas, width, height) {
      canvas.width = width;
      canvas.height = height;
    },
    destroy(canvas) {
      canvas.width = 0;
      canvas.height = 0;
    },
  },
});

```

### OffscreenCanvas for worker-based rendering

Render in a web worker (off the main thread) with `OffscreenCanvas`:

```tsx

// In a web worker:
const offscreenCanvas = new OffscreenCanvas(width, height);
const context = offscreenCanvas.getContext("2d");

await page.render({
  canvasContext: context,
  viewport,
}).promise;

// Transfer the bitmap back to main thread.
const bitmap = offscreenCanvas.transferToImageBitmap();
self.postMessage({ bitmap }, [bitmap]);

```

## Loading task events

Monitor document loading progress:

```tsx

const loadingTask = getDocument({ url: "large-document.pdf" });

loadingTask.onProgress = (progress) => {
  if (!progress.total) return; // Server may omit `Content-Length`.
  const percent = (progress.loaded / progress.total) * 100;
  console.log(`Loading: ${percent.toFixed(1)}%`);
  updateProgressBar(percent);
};

loadingTask.onPassword = (updateCallback, reason) => {
  // PDF is password-protected.
  const password = prompt("Enter PDF password:");
  updateCallback(password);
};

```

### Password-protected PDFs

Check `reason` against `PasswordResponses` to tell an initial password prompt apart from a retry after an incorrect one:

```tsx

import { PasswordResponses } from "pdfjs-dist";

const loadingTask = getDocument({ url: "protected.pdf" });

loadingTask.onPassword = (callback, reason) => {
  if (reason === PasswordResponses.NEED_PASSWORD) {
    const password = prompt("This PDF is password-protected. Enter password:");
    callback(password);
  } else if (reason === PasswordResponses.INCORRECT_PASSWORD) {
    const password = prompt("Wrong password. Try again:");
    callback(password);
  }
};

```

## Key points

- `rangeChunkSize` controls how much data PDF.js requests at once — increase for fast networks, and decrease for slow ones.

- `disableAutoFetch: true` only downloads page data when that page is scrolled to (saves bandwidth).

- `PDFDataRangeTransport` gives complete control over how PDF bytes are fetched.

- `renderTask.cancel()` is essential for smooth fast-scrolling performance.

- `loadingTask.onProgress` enables loading progress bars.

- `loadingTask.onPassword` handles password-protected PDFs.

- Use `OffscreenCanvas` for off-main-thread rendering in performance-critical apps.

## How Nutrient Web SDK handles this

Nutrient Web SDK ships with progressive loading, range-request handling, render prioritization, and memory management configured by default. The viewer initializes with a single `NutrientViewer.load()` call:

```js

const instance = await NutrientViewer.load({
  container: "#pdf-container",

  document: "document.pdf",
});

```

The viewer applies its own loading strategy — there’s no `GlobalWorkerOptions.workerSrc` to set, no `PDFDataRangeTransport` to subclass, and no separate `OffscreenCanvas` plumbing to maintain. If you need to tune behavior (caching, prefetch, network policy), it’s a configuration option rather than a custom transport.

## FAQ

#### Can I pass a <code>ReadableStream</code> to <code>getDocument</code>?

No. The `data` option only accepts `TypedArray | ArrayBuffer | string`. To stream bytes as they arrive, pass the URL (PDF.js will use HTTP `Range` requests automatically when the server supports them) or implement a custom `PDFDataRangeTransport`.

#### What does <code>disableAutoFetch</code> actually do?

With `disableAutoFetch: true`, PDF.js stops downloading the rest of the file once the initial range needed to display the current page has arrived. Pages that haven’t been scrolled to yet are fetched on demand. Use it on slow networks or when the user might not read the whole document — at the cost of a longer wait when they finally scroll.

#### When should I cancel a render task?

Cancel pending renders whenever the page they’re producing is no longer needed — scrolling past a page, zooming (which forces a fresh viewport), or rotating. Without cancellation, fast scrolling queues up renders that block the worker and freeze the UI. Catch `RenderingCancelledException` on the rejected `renderTask.promise` and treat it as a no-op.

#### Should I render in a worker with <code>OffscreenCanvas</code>?

Only if main-thread rendering is a measurable bottleneck for your app. PDF.js’s worker already parses PDFs off the main thread; `OffscreenCanvas` adds the *rasterization* step to that. The cost is shipping each frame back as an `ImageBitmap` via `postMessage`, which can outweigh the savings for typical viewers. Profile first.

#### How does PDF.js fall back when the server doesn’t support <code>Range</code> requests?

PDF.js downloads the entire file in one request. It then parses it once the download completes. You can force this behavior with `disableRange: true`. Without `Range` support, you also lose the ability to display the first page before the rest of the document has arrived.

#### Can I disable workers entirely?

Yes — pass `disableWorker: true` to `getDocument`. The library will run parsing on the main thread, which blocks the UI during loading and rendering. Use it only for debugging or for environments (like some test runners) where web workers aren’t available.

_See [Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) for streaming and progressive loading handled by the viewer, or follow the [migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-mozilla-pdfjs.md) to switch from PDF.js. [Talk to Sales](https://www.nutrient.io/contact-sales/?=sdk) about your requirements._
---

## Related pages

- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [`vector_store` holds your indexed documents (see the multimodal RAG post](/blog/agentic-rag.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [1. Extract and chunk the PDF.](/blog/chat-with-pdf.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [Creating And Filling Pdf Forms Programmatically In Javascript](/blog/creating-and-filling-pdf-forms-programmatically-in-javascript.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Viewer](/blog/document-viewer.md)
- [Document Watermarking](/blog/document-watermarking.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [Fillable Pdf](/blog/fillable-pdf.md)
- [How To Add Digital Signature To Pdf Using React](/blog/how-to-add-digital-signature-to-pdf-using-react.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [How To Build A Javascript Pdf Viewer](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Convert Docx To Pdf Using Javascript](/blog/how-to-convert-docx-to-pdf-using-javascript.md)
- [How To Convert Docx To Pdf Using Python](/blog/how-to-convert-docx-to-pdf-using-python.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [or](/blog/how-to-create-a-react-js-signature-pad.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [How To Extract Tables From Pdf And Images](/blog/how-to-extract-tables-from-pdf-and-images.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Javascript Pdf Libraries](/blog/javascript-pdf-libraries.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [Building WCAG 2.2, Section 508, and PDF/UA-compliant PDFs with an SDK](/blog/pdf-accessibility.md)
- [Pdf Extraction Benchmark Opendataloader Bench](/blog/pdf-extraction-benchmark-opendataloader-bench.md)
- [Pdf Extraction Document Case Studies](/blog/pdf-extraction-document-case-studies.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Document Outline Bookmarks Metadata](/blog/pdfjs-document-outline-bookmarks-metadata.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs Pdf Page Manipulation Pdf Lib](/blog/pdfjs-pdf-page-manipulation-pdf-lib.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Server Side Text Extraction](/blog/pdfjs-server-side-text-extraction.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [Process Flows](/blog/process-flows.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [or](/blog/sample-blog-updated.md)
- [Add DWS MCP Server to your Claude Code project.](/blog/teaching-llms-to-read-pdfs.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Is Document Processing](/blog/what-is-document-processing.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)

