ScreenshotNeo

BlogHTML to image & PDF

A Guide to Converting HTML to PDF

Convert a webpage to PDF by printing it in your browser or rendering it with Playwright or Puppeteer. Learn how to control styles, paper size, margins, and page breaks.

By the ScreenshotNeo team4 October 20269 min read

For a one-time PDF, open the page in a browser, choose Print, and select Save as PDF. For repeatable output, render the page with a browser automation library such as Playwright or Puppeteer. Their PDF methods use print CSS by default, so prepare print styles and set paper size, margins, and backgrounds deliberately.

1. Choose a conversion method

Need Starting point Consider
Save one page occasionally Browser print dialog Paper size, page range, margins, background graphics, and browser-specific options.
Generate PDFs repeatedly from JavaScript Playwright or Puppeteer Print versus screen styles, navigation readiness, page dimensions, deployment, and output checks.
Render HTML and CSS from a Python workflow Evaluate WeasyPrint Whether the document’s CSS and JavaScript requirements fit. Test with your actual documents.
Run a managed production workflow Evaluate hosted rendering providers Availability, data handling, security, pricing, output controls, and service limits.

No renderer is guaranteed to produce identical output for every site. Choose based on the page’s CSS and JavaScript, how much control you need over pagination, and the operational cost of running the renderer. Compare the actual PDFs you need to produce.

2. Save a webpage as PDF in a browser

  1. Open the page and wait for its important content to appear.
  2. Open the browser’s print command, commonly Ctrl+P on Windows/Linux or Cmd+P on macOS.
  3. Choose the browser’s PDF destination, such as Save as PDF.
  4. Set paper size, page range, margins, scale, and background graphics as needed.
  5. Preview each page for clipped content, awkward breaks, missing images, and unwanted navigation before saving.

Browser labels and available controls vary. If colors or backgrounds are missing, check the print dialog’s background graphics option. A page’s print stylesheet can also intentionally remove backgrounds or simplify colors.

3. Prepare HTML and CSS for printing

A print stylesheet lets a page adapt for paper. Hide controls that do not belong in a document, adjust spacing and colors, and guide page breaks. Inspect the output at the intended paper size; CSS rules are guidance to the renderer, so verify the resulting PDF.

@media print {
  nav,
  .no-print,
  button {
    display: none !important;
  }

  body {
    color: #111;
    background: #fff;
    font-size: 11pt;
  }

  a {
    color: inherit;
    text-decoration: underline;
  }

  h1, h2, h3 {
    break-after: avoid;
  }

  img, table, pre {
    break-inside: avoid;
  }

  .new-page {
    break-before: page;
  }
}

@page {
  size: A4;
  margin: 18mm 16mm;
}

Use semantic selectors that match your page. Avoid hiding content that readers need in the PDF. Long tables and code blocks may still need special layout treatment. If you specify page dimensions in CSS, check whether your renderer is configured to honor CSS page size over its own paper-format option.

4. Generate a PDF with Playwright

Install Playwright and its Chromium browser using the official setup instructions for your project. The example below uses Node.js ES modules, visits a page, waits for fonts and images to settle, and writes an A4 PDF. Replace the example URL and margins for your document. See the Playwright Page API for current options and support details.

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  await page.evaluate(async () => {
    await document.fonts.ready;
    const images = Array.from(document.images);
    await Promise.all(images.map((img) => {
      if (img.complete) return Promise.resolve();
      return new Promise((resolve) => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });
    }));
  });
  await page.pdf({
    path: 'page.pdf',
    format: 'A4',
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: '18mm', right: '16mm', bottom: '18mm', left: '16mm' }
  });
} finally {
  await browser.close();
}

networkidle can be unsuitable for pages with long-lived requests or polling. If navigation never reaches that state, wait for a known content selector or use a different navigation wait condition, then wait for the specific content your document requires.

Useful Playwright PDF options

Option Use Notes
path Save output to a file Omit when your environment uses the returned PDF buffer directly.
format Choose a paper preset Playwright documents Letter as the default. Set a format explicitly or use width and height.
width, height Set page dimensions Use supported unit-bearing values when a preset does not fit.
margin Set top, right, bottom, and left margins Use explicit units such as mm, in, or px.
printBackground Include CSS backgrounds Defaults to false. Enable when the design depends on backgrounds.
preferCSSPageSize Honor CSS @page dimensions Useful when the stylesheet owns paper sizing; check interaction with format and dimensions.
pageRanges Limit output to selected pages Use the documented range syntax for the installed version.
landscape Print in landscape orientation Choose it for wide tables or diagrams when that improves readability.
scale Adjust rendered size Check text size and clipping after changing it.

For screen styling instead of print styling, call await page.emulateMedia({ media: 'screen' }) before page.pdf(). Compare both outputs: screen styles may preserve the web layout, while print CSS may be designed specifically for paper.

5. Generate a PDF with Puppeteer

Puppeteer’s page.pdf() also renders with print CSS by default. The following Node.js example follows the same pattern: navigate, wait for assets, and define output settings. Consult the Puppeteer Page.pdf API for the options supported by your installed version.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle0' });
  await page.evaluate(async () => {
    await document.fonts.ready;
    await Promise.all(Array.from(document.images, (img) => {
      if (img.complete) return Promise.resolve();
      return new Promise((resolve) => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });
    }));
  });
  await page.pdf({
    path: 'page.pdf',
    format: 'A4',
    printBackground: true,
    preferCSSPageSize: true,
    margin: { top: '18mm', right: '16mm', bottom: '18mm', left: '16mm' }
  });
} finally {
  await browser.close();
}

For screen styling, use await page.emulateMediaType('screen') before creating the PDF. Printed colors may be adjusted by the rendering environment; Puppeteer documents using print color adjustment when exact CSS colors matter. Confirm the result in the generated file rather than assuming a setting guarantees a particular printer or viewer appearance.

6. Make the output reliable

  • Wait for the content you need. Navigation completion alone may not mean that a client-rendered page, embedded chart, or lazy image is ready. Wait for a meaningful selector or application signal.
  • Wait for fonts and images. Use document.fonts.ready and check image completion when those assets affect layout. Handle failed assets explicitly if their absence should fail the job.
  • Set output dimensions. Specify paper format or dimensions and margins instead of relying on defaults.
  • Choose print or screen media intentionally. The PDF methods default to print CSS. Emulate screen media only when the requirement is to preserve screen styling.
  • Decide whether backgrounds matter. Enable background printing only when required; inspect colors and contrast.
  • Validate the file. Check page breaks, clipped content, font loading, image inclusion, links, and the result at the target paper size.
  • Use representative input pages. Test pages with long content, tables, code, missing images, and unusual fonts before automating a larger workload.

7. Or skip the browser setup

ScreenshotNeo can return a webpage as a PDF with one API request. See the ScreenshotNeo API documentation for request options. For production use, replace the placeholder with your API key and URL.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.pdf
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("shot.pdf", "wb").write(r.content)
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(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.pdf', res);

For Node.js versions without Bun, read the response bytes and write them with your preferred file system API. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

8. Troubleshooting

Symptom Likely cause What to try
PDF looks different from the browser window The PDF renderer uses print media by default, so print CSS applies. Adjust @media print, or emulate screen media before generating the PDF if screen styling is required.
Backgrounds or colors are missing Background printing may be disabled, or print color adjustment changes colors. Enable printBackground or the browser’s background option, then inspect print color settings and the actual output.
Fonts are substituted or text wraps differently PDF generation ran before web fonts loaded, or the font request failed. Wait for document.fonts.ready; check font loading and network access, then review line wrapping.
Images are blank or incomplete Lazy loading, delayed requests, or failed image URLs. Wait for required images or content signals and check their load errors. Ensure the target page can access its assets.
Navigation wait hangs The page may keep connections open or make ongoing requests. Use a different navigation condition and wait for a specific selector or application-ready signal.
Content is clipped at page edges Paper size, margins, scale, or wide content do not fit. Set dimensions and margins explicitly; review wide tables and long code blocks, and consider landscape orientation.
Headings or rows split awkwardly Content exceeds the remaining page area or break rules do not fit the layout. Use print break rules such as break-inside: avoid selectively and inspect long elements that cannot fit on one page.
PDF page size ignores CSS @page The renderer’s format or dimensions take precedence. Check CSS page-size priority settings, including Playwright’s preferCSSPageSize, and render again.
Generated PDF is empty or stale The page may not have rendered its content, or a prior output file may be mistaken for the latest result. Wait for the expected content, check navigation status and output path, and validate the newly generated file.

9. Performance, reliability, and cost

Manual printing has little setup for occasional documents. Browser automation adds browser installation, launch time, memory use, page loading, and ongoing package/browser maintenance. The dossier provides no controlled speed or memory comparison between renderers, so benchmark representative documents in your own deployment before choosing capacity.

For repeatable jobs, bound navigation and job time, close pages and browsers in cleanup paths, and record failures with the target URL and stage that failed. Reuse browser processes where appropriate for your workload, while keeping page state isolated. Validate output before treating a job as successful. Hosted rendering can shift browser operations to a provider, but evaluate availability, data handling, security, service limits, and current pricing before adopting it.

For ScreenshotNeo, the published plans are Free: 1,000 shots/month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free. Every feature is on every plan. Only clean shots are billed; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers.

10. Frequently asked questions

Check the generated PDF with the links your workflow needs. Validate link behavior as part of output QA rather than assuming every rendering path handles every link the same way.

Can I convert a local HTML file?

Browser automation can navigate to local content when the runtime and file access are configured for it. For application pages, serving the HTML and assets through the same environment can avoid path and asset-loading surprises.

Will the PDF be accessible?

Successful rendering does not establish accessibility conformance. If accessibility is required, validate the PDF against the applicable requirements with a suitable review process.

Which renderer is fastest?

The sources do not establish a controlled speed comparison. Measure with your documents, runtime, and deployment conditions.

Sources