PDF.js in HTML: render a specific PDF page without a browser toolbar

An equipment handbook needs page 2 beside the booking instructions, rather than an entire PDF viewer inside the page. PDF.js can load the file, select that page, and draw it onto a canvas. Use a normal PDF embed when you want the browser's viewer; use PDF.js when your application needs to control rendering. A canvas alone does not include selectable text, links, or the viewer's accessibility features.

Keep the library and worker together

Get a PDF.js distribution from the project's getting-started page. Keep its main module and worker from the same release. In the small layout below, copy pdf.mjs and pdf.worker.mjs from that distribution into vendor/, then supply your own permitted PDF:

handbook-preview/
  index.html
  handbook.pdf
  vendor/
    pdf.mjs
    pdf.worker.mjs

Serve this folder over HTTP using your existing development server. Double-clicking index.html introduces local-file restrictions and does not reproduce a deployed module/worker setup. If the distribution includes additional support files needed by your document, retain and configure those too; these two module paths are not a promise that every PDF is self-contained.

Draw page 2 and wait for rendering

This original integration example assumes the supplied handbook has at least two pages. It calculates the rendering scale from the available width and gives high-density screens a larger canvas backing store:

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Equipment handbook preview</title>
<style>
  body { max-width: 42rem; margin: 1rem auto; padding: 0 1rem; }
  canvas { display: block; max-width: 100%; }
</style>
<h1>Before collecting equipment</h1>
<p><a href="handbook.pdf">Open the complete handbook PDF</a></p>
<p id="status" role="status">Loading handbook page 2…</p>
<div id="page"><canvas id="preview"
  aria-label="Visual preview of handbook page 2"></canvas></div>
<script type="module">
  import * as pdfjs from './vendor/pdf.mjs';
  pdfjs.GlobalWorkerOptions.workerSrc = './vendor/pdf.worker.mjs';
  const status = document.querySelector('#status');
  try {
    const pdf = await pdfjs.getDocument('handbook.pdf').promise;
    if (pdf.numPages < 2) throw new Error('Handbook has no page 2.');
    const page = await pdf.getPage(2);
    const box = document.querySelector('#page');
    const base = page.getViewport({ scale: 1 });
    const viewport = page.getViewport({ scale: box.clientWidth / base.width });
    const ratio = window.devicePixelRatio || 1;
    const canvas = document.querySelector('#preview');
    canvas.width = Math.floor(viewport.width * ratio);
    canvas.height = Math.floor(viewport.height * ratio);
    canvas.style.width = `${viewport.width}px`;
    canvas.style.height = `${viewport.height}px`;
    await page.render({
      canvasContext: canvas.getContext('2d'), viewport,
      transform: ratio === 1 ? null : [ratio, 0, 0, ratio, 0, 0]
    }).promise;
    status.textContent = `Showing PDF page 2 of ${pdf.numPages}.`;
  } catch (error) {
    status.textContent = 'Preview unavailable. Open the complete PDF above.';
    console.error(error);
  }
</script>
</html>

The document and rendering promises complete at different times. “PDF loaded” does not establish that a page has finished drawing. Mozilla's rendering examples show this distinction and warn against rendering multiple pages onto the same canvas simultaneously. A later Previous/Next implementation must wait for the current render or cancel it before reusing that canvas. This single-page example draws at its initial width; reload after resizing, or add a managed resize-and-render cycle.

Separate a missing file from a worker mismatch

Inspect the main module, worker, and PDF requests separately in your browser's Network panel. Each must return the expected file rather than an HTML login or error page. A worker version mismatch calls for a matching deployment of both modules; renaming a worker file does not change its version. The PDF.js FAQ covers version matching and cross-origin loading.

Keep the PDF on the same origin for the first integration. A PDF on another origin needs permission through that server's CORS configuration. A file opening successfully in a new browser tab does not establish that JavaScript on your origin may fetch it.

Decide whether the canvas is enough

The visible page is a drawing. An aria-label names the preview but does not expose the handbook's text or reading order. If readers need document search, selectable passages, accessible document navigation, or annotations, start from PDF.js's viewer components rather than expanding a bare canvas one feature at a time.

For a browser-controlled viewer, HTML PDF embeds provide the simpler route. For this controlled preview, finish by comparing the displayed page to page 2 in the original PDF, then open the complete-file link. Both should identify the same handbook revision.

Keep reading