Screenshots and PDFs with Puppeteer

Updated June 2026
Puppeteer captures pixel-perfect screenshots and generates high-quality PDF documents by leveraging Chrome's native rendering engine. Because it renders pages in a real browser, the output matches exactly what a user would see, with full support for CSS layouts, custom fonts, SVG graphics, and JavaScript-rendered content.

Screenshot capture and PDF generation are two of Puppeteer's most widely used features. Common applications include visual regression testing, social media preview image generation, invoice and report creation, website archiving, and automated content preview services. The process is straightforward for basic use cases, with enough configuration depth to handle complex requirements like custom page sizes, element-specific captures, and dynamic content rendering.

Step 1: Configure the Viewport

The viewport determines the dimensions of the browser window that Puppeteer renders. This directly affects how the page lays out content and how large your screenshots will be. Set the viewport using page.setViewport({ width: 1280, height: 720 }) before navigating to your target page.

The default viewport in Puppeteer is 800 by 600 pixels, which is narrower than most desktop screens and may trigger tablet or mobile layouts on responsive websites. For desktop-quality screenshots, set the width to 1280, 1440, or 1920 pixels depending on the breakpoint you want to capture. The height matters primarily for viewport-only screenshots; for full-page captures, Puppeteer automatically extends the capture to cover the entire scrollable page regardless of the viewport height.

The deviceScaleFactor option controls the pixel density of the output. The default value of 1 produces standard resolution images. Setting it to 2 renders at double resolution, producing Retina-quality output where each CSS pixel maps to a 2x2 block of actual pixels. A 1280-pixel-wide viewport with a scale factor of 2 produces a 2560-pixel-wide image. This is essential for generating images that look sharp on high-DPI displays, but it doubles the file size and rendering time.

For mobile screenshots, use Puppeteer's device emulation presets. The library includes profiles for dozens of common devices that configure viewport size, device scale factor, user agent string, and touch support in a single call. You can also create custom device profiles for any screen configuration you need to simulate.

Step 2: Capture Viewport and Full-Page Screenshots

The basic screenshot command is await page.screenshot({ path: 'output.png' }), which captures the visible viewport and saves it as a PNG file. The path can be absolute or relative to your script's working directory. If you omit the path, the method returns the image data as a Buffer that you can process in memory, send to an API, or write to a stream.

To capture the entire scrollable page rather than just the visible viewport, add fullPage: true to the options. Puppeteer scrolls through the page internally, stitching together the full content into a single image. This is useful for capturing long articles, product pages, or dashboards that extend well below the initial viewport. Be aware that very long pages can produce large image files, and some pages have content that loads lazily as you scroll, which may not render correctly in a single full-page capture.

The output format is PNG by default, which produces lossless images with transparency support. Switch to JPEG by specifying type: 'jpeg' in the options, and control compression with the quality parameter (a number from 0 to 100). JPEG files are typically 60 to 80 percent smaller than equivalent PNG files, making them better suited for web display where transparency is not needed.

For transparent backgrounds, set omitBackground: true in the screenshot options. This renders the page with a transparent background instead of white, which is useful for generating images that will be composited onto other backgrounds. This only works with PNG output since JPEG does not support transparency.

You can also clip the screenshot to a specific rectangular region using the clip option, which takes x, y, width, and height values in CSS pixels. This is a quick way to capture a particular area of the page without needing to target a specific DOM element.

Step 3: Capture Specific Elements

Instead of capturing the entire viewport, you can screenshot individual DOM elements. First, select the element with const element = await page.$('.target-element'), then call await element.screenshot({ path: 'element.png' }). Puppeteer calculates the element's bounding box automatically and crops the capture to fit exactly.

Element screenshots are valuable for capturing specific components like charts, data tables, cards, headers, or any other self-contained UI section. The output image dimensions match the element's rendered size, including padding and borders but excluding margins. If the element extends beyond the viewport, Puppeteer scrolls it into view before capturing.

When capturing elements that rely on surrounding context for styling, verify that the isolated screenshot looks correct. Some elements inherit background colors, fonts, or spacing from parent containers that will not be present in the cropped capture. You may need to apply explicit background colors or padding to the target element to ensure the screenshot is self-contained and visually complete.

For dynamic content that changes after the initial render, such as charts that animate on load or counters that increment, wait for the content to reach its final state before capturing. Use page.waitForFunction() to check for completion indicators like animation-end classes, specific text values, or data attributes that signal rendering is done.

Step 4: Generate PDF Documents

Puppeteer generates PDFs using Chrome's built-in print rendering engine, the same engine used when you print a web page from the browser. Call await page.pdf({ path: 'output.pdf' }) to convert the current page to PDF. The output includes selectable text, working hyperlinks, and proper document structure, unlike screenshot-based PDF approaches that produce flat images.

Note that PDF generation only works in headless mode. If you launched the browser with headless: false for debugging, switch back to headless before generating PDFs.

The format option sets the page size using standard names: 'Letter' (8.5 by 11 inches), 'A4' (210 by 297 millimeters), 'Legal', 'Tabloid', and others. For custom sizes, use the width and height options with CSS unit values like '8.5in', '210mm', or '600px'. Margins are set through the margin option with top, right, bottom, and left values in the same CSS units.

Custom headers and footers are added through the headerTemplate and footerTemplate options, which accept HTML strings. You must also set displayHeaderFooter: true to enable them. Within these templates, Puppeteer replaces special class names with dynamic content: date inserts the current date, title inserts the page title, url inserts the page URL, pageNumber inserts the current page number, and totalPages inserts the total page count. The header and footer templates receive their own isolated styles, so you need to include inline CSS for font sizing, alignment, and spacing directly in the template HTML.

The printBackground option (default false) controls whether CSS background colors and images are included in the PDF output. For most document generation use cases, you want this set to true so that styled backgrounds, colored table rows, and background images appear in the output.

CSS print stylesheets (@media print) control the PDF layout. You can define page breaks with page-break-before and page-break-after properties, hide navigation elements or sidebars that are irrelevant in printed output, adjust font sizes for print readability, and control column layouts for multi-column printed pages. Well-designed print stylesheets produce dramatically better PDF output than relying on Puppeteer to render the screen version of a page as-is.

Step 5: Build a Rendering Service

For production screenshot or PDF generation, build a service that manages browser instances efficiently, handles concurrent requests, and recovers from failures.

Browser pooling is the foundation of an efficient rendering service. Instead of launching and closing Chrome for each request, maintain a pool of browser instances that accept rendering tasks. Each browser can handle multiple tabs concurrently, though you should limit concurrent tabs per browser to three or four to prevent memory exhaustion. Recycle browser instances after a fixed number of tasks or time interval to prevent memory leaks from accumulating.

Input validation protects your service from abuse. Validate URLs before navigating to them, set maximum viewport dimensions to prevent unreasonable resource consumption, and impose timeout limits on page loads and rendering operations. For services that accept HTML input directly (rendering a provided HTML string rather than a URL), sanitize the input to prevent server-side request forgery and other injection attacks.

Caching is valuable when the same pages are rendered repeatedly. Use a cache keyed by URL, viewport dimensions, and output format. Set reasonable cache expiration times based on how frequently the source content changes. For frequently requested screenshots of relatively static pages, caching can reduce rendering load by 90 percent or more.

Error handling needs to cover browser crashes, page load timeouts, out-of-memory conditions, and malformed pages. Each failure mode requires a different response: retry for transient errors, restart the browser for crashes, return an error response for permanently broken URLs, and implement circuit breakers to stop retrying URLs that fail consistently.

Key Takeaway

Puppeteer produces browser-quality screenshots and PDFs because it uses Chrome's actual rendering engine. For one-off captures, the API is simple. For production services, invest in browser pooling, caching, and proper error handling to manage Chrome's resource demands.