---
title: "Generate PDF thumbnail previews with pdf2pic in Node.js"
canonical_url: "https://www.nutrient.io/blog/pdfjs-generating-pdf-thumbnails-pdf2pic/"
md_url: "https://www.nutrient.io/blog/pdfjs-generating-pdf-thumbnails-pdf2pic.md"
last_updated: "2026-08-07T10:26:59.750Z"
description: "Generate PDF page thumbnails with pdf2pic in Node.js — single-page previews, multipage batches, cloud storage upload, and a PDF.js browser-side alternative."
---

**TL;DR**

- `fromBuffer(pdfBuffer, options)` from `pdf2pic` converts any page of a PDF to an image; pick `responseType: "buffer"` for cloud uploads or `"base64"` for inline embedding.

- For browsers, render to a canvas at a small scale (around `0.3`) and `canvas.toDataURL("image/jpeg", 0.8)`.

- `pdf2pic` requires GraphicsMagick or ImageMagick on the host — package it into your Docker image or container.

## Prerequisites

This guide is server-side first, with a browser-side variant at the end. You’ll need Node.js, plus a system-level image processing tool (`pdf2pic` shells out to either GraphicsMagick or ImageMagick).

You’ll also need:

- Node.js 14 or later

- `pdf2pic` from npm

- GraphicsMagick (`brew install graphicsmagick`/`apt-get install graphicsmagick`) — or ImageMagick as an alternative

- Ghostscript (`brew install ghostscript`/`apt-get install ghostscript`) — required for PDF parsing alongside GraphicsMagick

- For the browser variant — a loaded `PDFDocumentProxy` from `pdfjs-dist`

When building a document management system, you often need thumbnail images of PDF pages for preview galleries, search results, or file cards. `pdf2pic` converts PDF pages to images in Node.js.

## Install

Install `pdf2pic` from npm:

```bash

npm install pdf2pic

```

**System dependencies:** `pdf2pic` shells out to GraphicsMagick (or ImageMagick) **and** Ghostscript. You need both — Ghostscript handles the PDF parsing, while GraphicsMagick handles the image conversion.

```bash

# macOS

brew install graphicsmagick ghostscript

# Ubuntu/Debian

sudo apt-get install graphicsmagick ghostscript

```

## Basic usage: First page preview

`fromBuffer` takes a PDF buffer and a set of conversion options. It then returns a function for converting individual pages:

```tsx

import { fromBuffer } from "pdf2pic";

async function generatePreview(pdfBuffer) {
  const options = {
    preserveAspectRatio: true,
    height: 600,
    quality: 80,
    format: "png",
  };

  const convert = fromBuffer(pdfBuffer, options);

  // Convert page 1, get result as a Buffer.
  const output = await convert(1, { responseType: "buffer" });

  return output.buffer; // PNG image as Buffer.
}

```

## Options

| Option                | Type    | Description                               |
| --------------------- | ------- | ----------------------------------------- |
| `width`               | number  | Output width in pixels                    |
| `height`              | number  | Output height in pixels                   |
| `preserveAspectRatio` | Boolean | Maintain original aspect ratio            |
| `quality`             | number  | Image quality (1–100)                     |
| `format`              | string  | Output format: `"png"`, `"jpeg"`, `"gif"` |
| `density`             | number  | DPI for rendering (default: 72)           |

## Response types

The `responseType` option controls what shape `convert()` returns the result in:

```tsx

// Get as Buffer (for storage/upload).
const output = await convert(1, { responseType: "buffer" });
output.buffer; // Buffer

// Get as base64 string (for embedding in HTML).
const output = await convert(1, { responseType: "base64" });
output.base64; // string

// Save to file.
const convert = fromBuffer(pdfBuffer, {...options,
  savePath: "./thumbnails",
  saveFilename: "preview",
});
const output = await convert(1);
output.path; // "./thumbnails/preview.1.png"

```

## Multiple pages

Looping over `convert()` with each page number generates a thumbnail for every page in the document:

```tsx

async function generateThumbnails(pdfBuffer, pageCount) {
  const convert = fromBuffer(pdfBuffer, {
    preserveAspectRatio: true,
    height: 200,
    quality: 60,
    format: "jpeg",
  });

  const thumbnails = [];
  for (let page = 1; page <= pageCount; page++) {
    const output = await convert(page, { responseType: "buffer" });
    thumbnails.push({
      page,
      image: output.buffer,
    });
  }

  return thumbnails;
}

```

For large documents, `pdf2pic` also exposes a `.bulk()` method that takes an array of page numbers (or `-1` for all pages) and returns the same result objects in a single call:

```tsx

const convert = fromBuffer(pdfBuffer, options);
const results = await convert.bulk(-1, { responseType: "buffer" });

```

## Integration: Upload preview to cloud storage

The generated buffer can be uploaded directly to a storage bucket, without writing it to disk first:

```tsx

async function processNewPdf(pdfBuffer, fileId) {
  // Generate preview.
  const preview = await generatePreview(pdfBuffer);

  // Upload to cloud storage (e.g. Google Cloud Storage).
  await bucket.file(`previews/${fileId}.png`).save(preview, {
    contentType: "image/png",
  });
}

```

## Alternative: Browser-side thumbnails with PDF.js

If you need thumbnails in the browser (not server-side), use PDF.js directly:

```tsx

async function renderPageToCanvas(pdfDocument, pageNumber, scale = 0.3) {
  const page = await pdfDocument.getPage(pageNumber);
  const viewport = page.getViewport({ scale });

  const canvas = document.createElement("canvas");
  canvas.width = viewport.width;
  canvas.height = viewport.height;

  await page.render({
    canvasContext: canvas.getContext("2d"),
    viewport,
  }).promise;

  return canvas.toDataURL("image/jpeg", 0.8);
}

```

## Key points

- `pdf2pic` is the simplest Node.js solution for PDF-to-image conversion.

- `pdf2pic` requires GraphicsMagick or ImageMagick as a system dependency.

- `fromBuffer` is the most common entry point for server-side processing.

- Use `responseType: "buffer"` for cloud storage uploads.

- For browser-side thumbnails, use PDF.js’s `page.render()` with a small scale factor.

- Keep `quality` at 60–80 for thumbnails — no need for full quality on small images.

## How Nutrient Web SDK handles thumbnails

Nutrient Web SDK ships a built-in thumbnail sidebar that’s enabled with a single state change — no rendering, sizing, or DOM management required on your end:

```js

instance.setViewState((v) =>
  v.set("sidebarMode", NutrientViewer.SidebarMode.THUMBNAILS),
);

```

For server-side thumbnail generation, [Nutrient Document Engine](https://www.nutrient.io/sdk/document-engine/) exposes a PDF-to-image REST API that produces page images without GraphicsMagick or ImageMagick on the host.

<!--  Pair it with the [file format conversion pipeline][conversion-post] when you need to generate previews for non-PDF uploads. -->

[Learn more about Nutrient Web SDK](https://www.nutrient.io/sdk/web-overview/) | [Thumbnail sidebar guide](https://www.nutrient.io/guides/web/user-interface/sidebar/thumbnail-preview.md) | [PDF-to-image conversion](https://www.nutrient.io/guides/web/conversion/pdf-to-image.md) | [Migration guide](https://www.nutrient.io/guides/web/about/migration-guides/migrating-from-mozilla-pdfjs.md) | [Contact Sales](https://www.nutrient.io/contact-sales/?=sdk)

## FAQ

#### Why does <code>pdf2pic</code> need GraphicsMagick or ImageMagick installed?

`pdf2pic` is a thin Node.js wrapper that shells out to a system image-processing binary — it doesn’t rasterize PDFs itself. GraphicsMagick is usually faster and lighter; ImageMagick is more widely available. If you can’t install either (serverless functions, sandboxed environments), use the browser-side PDF.js variant, or call a hosted PDF-to-image API instead.

#### What <code>density</code> should I set for thumbnails?

For small thumbnails (200–600 px tall), a density of 100–150 is plenty. Higher values produce sharper images but take longer to render and consume more memory. For print-quality output, 300 is the conventional target — but you almost never need that for a sidebar preview.

#### JPEG, PNG, or WebP for thumbnails?

JPEG at quality 60–80 is the sweet spot for photos and dense pages — smallest file sizes and acceptable visual fidelity. PNG preserves crisp edges on text-heavy or line-art pages but balloons file sizes. `pdf2pic` doesn’t natively support WebP; you’d need to convert with `sharp` after rendering if size matters.

#### Can I generate all page thumbnails in parallel?

Technically yes (`Promise.all` over your `convert(page)` calls), but GraphicsMagick is single-process, and you’ll mostly be CPU-bound or I/O-bound on temp file writes. For documents with more than a handful of pages, a serial loop is just as fast and consumes less memory. Save parallelism for batching across multiple documents.

#### How do I generate thumbnails in serverless/Lambda environments?

GraphicsMagick isn’t available in standard Lambda runtimes. Your options are: (1) build a Lambda layer with the GraphicsMagick binary, (2) use container-based Lambda with your own image, (3) skip `pdf2pic` entirely and call a hosted PDF-to-image API. For low-volume cases, browser-side PDF.js rendering is often simpler.

#### The browser thumbnail looks blurry on Retina screens — why?

At `scale: 0.3`, the canvas’s intrinsic resolution matches its CSS size — but on a 2x Retina display, the browser stretches each canvas pixel across two physical pixels. To get crisp output, multiply the canvas’s intrinsic `width`/`height` by `window.devicePixelRatio` while keeping the CSS dimensions the same. See [PDF.js coordinate systems](https://www.nutrient.io/blog/pdfjs-coordinate-systems-pdf-to-screen.md) for the full HiDPI pattern.

<!-- [conversion-post]: /blog/pdfjs-file-format-conversion-to-pdf/ -->
---

## 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 Extraction Confidence Scores](/blog/document-extraction-confidence-scores.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)
- [How To Convert Html To Pdf Using Wkhtmltopdf And Csharp](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-csharp.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)
- [How To Merge Pdfs Using Javascript](/blog/how-to-merge-pdfs-using-javascript.md)
- [How To Ocr Pdfs In Linux](/blog/how-to-ocr-pdfs-in-linux.md)
- [How To Print Pdf In Csharp](/blog/how-to-print-pdf-in-csharp.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 Editors](/blog/javascript-pdf-editors.md)
- [Javascript Pdf Libraries](/blog/javascript-pdf-libraries.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [or](/blog/merge-pdfs.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 Accessibility Structtree Printing](/blog/pdfjs-accessibility-structtree-printing.md)
- [Pdfjs Advanced Loading Streaming Workers](/blog/pdfjs-advanced-loading-streaming-workers.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)
- [Using Yarn](/blog/react-pdf-editor.md)
- [or](/blog/sample-blog-updated.md)
- [Sdk Product Updates Q2 2026](/blog/sdk-product-updates-q2-2026.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)
- [Define the HTML part of the document.](/blog/top-10-ways-to-generate-pdfs-in-python.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [or](/blog/top-js-pdf-libraries.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 Intelligent Document Processing](/blog/what-is-intelligent-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)

