How to Render a Webpage to PDF with a Custom Paper Size
Set a PDF’s page size with CSS or browser automation. Learn how Puppeteer and Playwright handle dimensions, margins, print styles, and common PDF issues.
To render a webpage as a PDF with a custom paper size, set the page dimensions either in the page’s print stylesheet with CSS @page, or in your browser automation code with explicit width and height. Use CSS when the document’s print styles should own the page size. Use Puppeteer or Playwright options when the generating script should control it. If CSS dimensions should win over automation settings, enable preferCSSPageSize: true.
The examples below use US Letter dimensions (8.5 × 11 inches) and 12 mm margins. Replace these with the dimensions your output requires. Use physical units such as in, mm, or cm to make the intended paper dimensions explicit.
1. Choose whether CSS or your script controls the paper
| Approach | Use it when | What controls the dimensions |
|---|---|---|
CSS @page |
The page’s print stylesheet should define its paper size and margins, including for other print workflows. | The site stylesheet, when the PDF API is configured to honor it. |
| API width and height | Your automation job must impose consistent output dimensions regardless of the site’s print CSS. | The Puppeteer or Playwright PDF options. |
Take care when both the page and script specify sizes. Puppeteer and Playwright give a supplied format precedence over width and height. CSS @page size takes precedence when preferCSSPageSize is enabled. Its default is false, so CSS page sizing does not take precedence and content is scaled to fit the API paper size. Avoid contradictory settings unless you have a specific reason for them. [Puppeteer PDFOptions] [Playwright PDF generation]
2. Define a custom size with CSS
Add an @page rule to the stylesheet used when printing. Its size descriptor sets the page box’s dimensions and orientation; margin sets the space between the page edge and content.
/* print.css, or a stylesheet included by the page */
@page {
size: 8.5in 11in;
margin: 12mm;
}
@media print {
.screen-only {
display: none;
}
}
The two dimensions are width followed by height. For a landscape sheet, reverse them. Use the margin rule to reserve space for page content; it is not the same as shrinking the paper itself. CSS paged-media support can vary across browser versions, so inspect the resulting PDF in the browser version and environment you plan to use. [MDN: @page]
3. Render the PDF with Puppeteer
Install Puppeteer in a Node.js project with npm install puppeteer. The following complete script opens a page and writes a PDF using explicit dimensions:
// save as render-puppeteer.mjs
import puppeteer from 'puppeteer';
const url = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'page.pdf',
width: '8.5in',
height: '11in',
margin: {
top: '12mm',
right: '12mm',
bottom: '12mm',
left: '12mm',
},
printBackground: true,
});
} finally {
await browser.close();
}
Run it with node render-puppeteer.mjs https://example.com. The PDF API uses print CSS by default. If the document’s @page rule should control paper size, add preferCSSPageSize: true and remove conflicting format settings. If you want the page styled as it appears on screen, call await page.emulateMediaType('screen') before page.pdf(). Puppeteer’s PDF reference documents print media and notes that colors are adjusted for printing by default; use the CSS property -webkit-print-color-adjust when exact print colors matter. [Puppeteer: Page.pdf()]
4. Render the PDF with Playwright
Install Playwright with npm install playwright and install its browser with npx playwright install chromium. This runnable script uses Chromium to produce the same Letter-sized output:
// save as render-playwright.mjs
import { chromium } from 'playwright';
const url = process.argv[2] ?? 'https://example.com';
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.pdf({
path: 'page.pdf',
width: '8.5in',
height: '11in',
margin: {
top: '12mm',
right: '12mm',
bottom: '12mm',
left: '12mm',
},
printBackground: true,
});
} finally {
await browser.close();
}
Run it with node render-playwright.mjs https://example.com. To use CSS @page sizing, set preferCSSPageSize: true. Playwright, like Puppeteer, uses print CSS for PDF generation and omits background graphics unless printBackground: true is set. Unlabeled numeric width and height values are interpreted as pixels; use strings with units to avoid ambiguity. [Playwright: PDF generation]
5. Set margins, orientation, and backgrounds deliberately
- Width and height: Provide both dimensions with physical units when setting the size through the API. In CSS, use
@page { size: width height; }. Reverse the values for landscape. - Margins: Set all four sides in the PDF API or use the CSS
@pagemargin. Check that the content fits inside the printable area after margins are applied. - Backgrounds: Both APIs omit background graphics by default. Enable
printBackground: trueif the PDF needs background colors or images. This can increase output size. - Print versus screen styling: PDF generation uses print media. Add or adjust
@media printrules for content visibility and layout. Puppeteer can switch to screen styling by emulating the screen media type before PDF creation. - One source of truth: If the script owns dimensions, set API width and height and avoid an overriding CSS size. If CSS owns them, enable
preferCSSPageSizeand verify the resulting page dimensions.
6. cURL, Python, and Node.js alternatives
For a one-off conversion from a shell, Python, or Node.js without managing a local browser, ScreenshotNeo accepts a URL and returns a PDF. See the ScreenshotNeo API documentation for request options, including PDF paper size, margins, landscape orientation, and page ranges.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d format=pdf \
-o page.pdf
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"format": "pdf",
},
timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
format: 'pdf',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('page.pdf', Buffer.from(await res.arrayBuffer())));
7. Troubleshoot common PDF sizing problems
| Symptom | Likely cause | Fix |
|---|---|---|
| The PDF has the wrong dimensions. | A format option overrides API width and height, or CSS sizing is not being preferred. |
Remove the conflicting format, or set preferCSSPageSize: true when the CSS @page size should win. |
| Content appears scaled down or has unexpected whitespace. | The API’s paper size and CSS page size conflict; the default behavior scales content to fit the API size. | Choose one source of dimensions. Enable CSS size preference if CSS should own the paper. |
| Background colors or images are missing. | Background printing is disabled by default. | Set printBackground: true. |
| The PDF layout differs from the browser screenshot. | PDF generation uses print media, which can activate different styles and hide screen-only content. | Update @media print rules, or emulate screen media in Puppeteer if screen styling is the desired output. |
| Content is clipped at page edges. | Margins, fixed-width content, or print styles leave too little usable area. | Reduce margins, make content responsive to the printable width, and inspect page breaks in the generated PDF. |
| Numeric dimensions produce an unexpectedly small or large page. | Unlabeled API numbers are treated as pixels. | Use unit-bearing values such as '8.5in' or '210mm'. |
| A CSS page-size rule has no visible effect. | The browser may not support the relevant paged-media behavior as expected, or the PDF API is not configured to prefer CSS sizing. | Enable preferCSSPageSize, check that the print stylesheet loads, and validate in the target browser version. |
8. Performance, reliability, and cost
Local Puppeteer or Playwright gives your application direct control over browser launch, navigation, and PDF options. It also means your environment must install and run the browser, and each render depends on the target site’s load behavior. Use navigation waits suited to the page: waiting for a fully quiet network can stall on pages with persistent requests, so choose a less strict navigation condition or add an explicit wait for the content your PDF needs.
For repeatable output, keep the browser version and print styles consistent, use explicit physical units, and review PDFs for page breaks, clipping, and headers or footers in the target environment. Browser rendering can vary, and MDN cautions that paged-media features have differing support. Do not assume that a valid PDF response guarantees the page content rendered as intended.
Local browser automation has no per-render API charge from a screenshot service, but it uses your compute, storage, and maintenance time. A hosted rendering API trades browser operations for a per-plan allowance. ScreenshotNeo offers 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Only clean shots are billed: bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Plan details and features are available at ScreenshotNeo.
Or skip the browser setup
ScreenshotNeo can render a URL to PDF with one API request; configure the PDF paper size, margins, orientation, and page ranges using the API documentation. Here is the basic request:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d format=pdf \
-o page.pdf
Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.
FAQ
Can CSS set both the PDF paper size and margins?
Yes. Use the size and margin descriptors in an @page rule. Configure the PDF API to prefer CSS page sizing if its dimensions should govern the output.
Should I use format or custom width and height?
Use a built-in format when it matches the desired paper. Use width and height for a custom size. Do not set a conflicting format because it takes precedence over those dimensions.
Why does my PDF omit the page background?
Background graphics are omitted by default. Enable printBackground: true when you need them.
Does this produce a PDF of exactly what I see on screen?
Not by default: PDF generation uses print media. Print styles can change layout, visibility, and colors. In Puppeteer, emulate screen media before generating the PDF when screen styling is required, then inspect the output.


