Generate PDF thumbnail previews with pdf2pic in Node.js
Table of contents
pdf2pic in Node.js. This tutorial covers single-page previews, batch thumbnail generation, cloud storage integration, and a browser-side alternative using PDF.js canvas rendering.
fromBuffer(pdfBuffer, options)frompdf2picconverts any page of a PDF to an image; pickresponseType: "buffer"for cloud uploads or"base64"for inline embedding.- For browsers, render to a canvas at a small scale (around
0.3) andcanvas.toDataURL("image/jpeg", 0.8). pdf2picrequires 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
pdf2picfrom 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
PDFDocumentProxyfrompdfjs-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:
npm install pdf2picSystem dependencies: pdf2pic shells out to GraphicsMagick (or ImageMagick) and Ghostscript. You need both — Ghostscript handles the PDF parsing, while GraphicsMagick handles the image conversion.
# macOSbrew install graphicsmagick ghostscript
# Ubuntu/Debiansudo apt-get install graphicsmagick ghostscriptBasic usage: First page preview
fromBuffer takes a PDF buffer and a set of conversion options. It then returns a function for converting individual pages:
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:
// 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:
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:
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:
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:
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
pdf2picis the simplest Node.js solution for PDF-to-image conversion.pdf2picrequires GraphicsMagick or ImageMagick as a system dependency.fromBufferis 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
qualityat 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:
instance.setViewState((v) => v.set("sidebarMode", NutrientViewer.SidebarMode.THUMBNAILS),);For server-side thumbnail generation, Nutrient Document Engine exposes a PDF-to-image REST API that produces page images without GraphicsMagick or ImageMagick on the host.
Learn more about Nutrient Web SDK | Thumbnail sidebar guide | PDF-to-image conversion | Migration guide | Contact Sales
FAQ
pdf2pic 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.
density 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 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.
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.
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.
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 for the full HiDPI pattern.