Use Puppeteer to open a fully qualified URL in a bundled browser, wait for the page state your target site needs, then call page.pdf(). Puppeteer generates PDF output with print CSS by default; the example below saves an A4 PDF and closes the browser even if navigation or rendering fails.
Install Puppeteer and create a PDF
From an existing Node.js project, install Puppeteer:
As an Amazon Associate I earn from qualifying purchases.
npm install puppeteer
Puppeteer is guaranteed to work with its bundled browser; using a different browser is at your own risk. The official PDF options reference used here reports Puppeteer version 25.12.0, so check the documentation for the version installed in your project if defaults or behavior differ. LaunchOptions
Save the following as url-to-pdf.mjs and run it with node url-to-pdf.mjs. Replace the example URL with the page you want to render.
#1 Best Overall
import puppeteer from 'puppeteer';
const url = 'https://example.com';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const response = await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 30_000,
});
if (response && !response.ok()) {
throw new Error(`Navigation failed with HTTP ${response.status()}`);
}
await page.pdf({
path: 'page.pdf',
format: 'A4',
printBackground: true,
});
} finally {
await browser.close();
}
The URL passed to page.goto() should include its scheme, such as https://. Navigation can resolve after redirects with the response for the final destination; a resolved navigation does not necessarily mean the response was successful, which is why the example checks its status. The PDF is written to page.pdf in the current working directory. page.goto() · Getting started
Choose when the page is ready
page.goto() defaults to the load lifecycle condition. Its waitUntil option accepts a lifecycle value or an array of values; if you pass an array, all listed conditions must occur. The right choice depends on the site, and the documentation does not prescribe one universal readiness condition. page.goto() · WaitForOptions
Rank #2
| Readiness choice | When it can help | Trade-off |
|---|---|---|
load |
When the page’s load event is an adequate signal for the content you need. | It may not mean a client-rendered application has finished updating. |
networkidle2 |
When waiting for a quieter network is appropriate for the site. | Pages that keep network requests active may not reach a network-idle condition promptly. |
| Page-specific signal | When a known element or application-ready state indicates the content is ready; navigate, then wait for that signal. | You must identify a reliable signal for the target page. There is no generic selector that fits every site. |
If a page keeps polling, streaming, or otherwise making requests, a network-idle wait can be unsuitable. Choose a lifecycle condition that fits the page, then wait for a known element or application signal when navigation completion alone is insufficient. Avoid treating an arbitrary delay as proof that dynamic content is ready.
Set print layout, page size, and PDF options
page.pdf() uses print CSS media by default. This can produce a layout different from the one visible in a browser window. To render with screen CSS instead, call await page.emulateMediaType('screen') before page.pdf(). Set printBackground: true when the PDF should include background graphics; print rendering may otherwise alter colors. Page class · PDFOptions, Puppeteer 25.12.0
Rank #3
| Option | What it controls |
|---|---|
format |
Named paper size. The documented default is letter; the example selects A4. |
landscape |
Whether the page uses landscape orientation. |
margin |
PDF page margins. |
path |
Output file path. A relative path is resolved from the current working directory. |
pageRanges |
The page range or ranges to include. |
scale |
The scale applied to the page during PDF output. |
preferCSSPageSize |
Whether CSS page dimensions take priority over the PDF’s format, width, or height options. The documented default is false. |
waitForFonts |
Whether to wait for fonts before generating the PDF. The documented default is true. |
For a document with its own @page dimensions, use preferCSSPageSize: true so CSS page sizing takes priority. Otherwise, select the intended paper format and use margins or orientation options as needed. The documented PDF timeout is 30 seconds; if rendering takes longer, review the current PDF options reference for the installed version. PDFOptions
Handle navigation errors and unsuccessful responses
page.goto() can reject for an invalid URL, SSL error, timeout, unreachable server, or failed main-resource load. It can also resolve with an HTTP error response, so inspect the returned response status when the script must reject unsuccessful pages. In headless shell mode, navigation to a PDF document is not supported. page.goto()
Rank #4
- Invalid URL: Include a valid scheme such as
https://and check that the address is correctly formed. - Navigation timeout: The page may be slow, unreachable, or waiting on an unsuitable lifecycle condition. Check the URL and server availability, then choose a more suitable readiness strategy; increase the navigation timeout only when a longer wait is justified.
- HTTP error status: Check
response.status()and handle the status explicitly rather than treating a resolved promise as success. - Blank or incomplete PDF: The page may need a page-specific readiness signal, or its print stylesheet may hide or rearrange content. Inspect the page’s print layout and wait for the application’s content to be ready.
- Missing backgrounds or unexpected colors: Enable
printBackground: truewhen background graphics are required, and remember that print CSS is used by default. - PDF timeout: Rendering may exceed the documented 30-second PDF timeout. Check the document and current version’s PDF options before changing the workflow.
Close the browser in a finally block, as in the example, so it is shut down after success or failure. Puppeteer does not document a universal performance or reliability figure for this workflow; actual capture time depends on the page, its resources, and readiness behavior.
Render HTML you already have
If the input is HTML already available to your script, use page.setContent(html) to set the page content before calling page.pdf(). This is not equivalent to navigating to a remote URL: it does not by itself establish a production recipe for loading external resources, handling authentication, or determining when an application is ready. page.setContent()
Or skip the browser setup
If you need a screenshot or PDF without managing Puppeteer and its browser, ScreenshotNeo provides a website screenshot API and MCP server. For a PDF, make one GET request with the URL and request PDF output as described in the API documentation. The following cURL example shows the request shape for a screenshot; adapt the output options for PDF using the docs:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
- It accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents, including Claude, Cursor, and other MCP clients. - The Free plan includes 1,000 screenshots per month with no card required. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
Frequently Asked Questions
Can Puppeteer save a PDF directly from a URL?
Yes. Navigate with page.goto() and generate the file with page.pdf().
Free tools Windows power users keep installed
One-click scans. No signup required.
Does page.pdf() use screen CSS?
No. It uses print CSS by default; call page.emulateMediaType('screen') first to use screen media.
Can Puppeteer open a PDF URL with page.goto()?
Not in headless shell mode; the page.goto() documentation lists that as unsupported.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




