How to Save a Webpage as a PDF with CSS Print Styles
Save a webpage as a PDF from your browser, then use CSS print styles to control page size, margins, layout, and page breaks.
To save a webpage as a PDF, open the browser’s print dialog and choose its PDF destination or output option. If you control the page, CSS print styles can hide screen-only controls, simplify the layout, set page dimensions and margins, and guide where content breaks across pages. The exact print-dialog labels depend on your browser and operating system.
Print media styles apply to printed output, including a page rendered to PDF. The browser’s print preview is the practical place to check how those rules and its own print settings combine. MDN’s printing guide covers print stylesheets and print media.
1. Save a webpage as a PDF in your browser
- Open the webpage you want to save.
- Open the browser’s print dialog using its print command.
- Choose a PDF destination or output option in that dialog.
- Review the preview, including page count, margins, scaling, and page breaks.
- Save or export the PDF using the dialog’s controls.
Control names and options vary by browser and operating system. Print preview also reflects the page’s CSS and the settings selected in the dialog. Check the final PDF rather than assuming the preview or CSS alone determines every detail.
2. Add print-only CSS to your page
Use @media print to apply rules only when the page is printed or rendered for print preview. Remove elements that are useful on screen but distracting on paper, and switch screen-oriented layouts to a document-friendly flow.
/* Add to your site's stylesheet. */
@media print {
nav,
header,
footer,
.no-print {
display: none !important;
}
main {
width: auto;
max-width: none;
margin: 0;
}
body {
color: #000;
background: #fff;
font: 12pt/1.45 Georgia, serif;
}
a {
color: inherit;
text-decoration: none;
}
/* Avoid forcing a print color treatment on selected elements. */
.preserve-print-color {
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
}
The selectors are examples: replace nav, header, footer, main, and .no-print with elements and classes in your own page. The !important declaration can help override screen styles, but inspect the cascade if a rule still does not apply. Print color handling also depends on browser and print settings, so preview it.
You can keep print rules in a separate stylesheet by linking it with the print media value:
<link href="/path/to/print.css" media="print" rel="stylesheet" />
Use either a print block in your main stylesheet or a dedicated print stylesheet. A separate file can make print behavior easier to maintain when the rules are substantial. See MDN’s @media reference.
3. Set page size, orientation, and margins
The @page at-rule controls the page box used for paged output. For example, the following requests portrait A4 pages with 18 mm margins:
@page {
size: A4 portrait;
margin: 18mm;
}
Other common size values include letter and legal; orientation can be portrait or landscape. Choose dimensions that suit the document’s readers and content. Browser support for parts of @page can vary, and print-dialog settings can also affect the result. Confirm the dimensions and fit in the target browser’s preview.
For example, a landscape document can use:
@page {
size: A4 landscape;
margin: 12mm;
}
Reference: MDN’s @page reference and its size descriptor reference.
4. Guide page breaks and keep related content together
Printed content is fragmented across discrete pages. You can suggest where a new page begins or ask the browser to avoid splitting a component, such as a card or table row group, when possible.
/* Begin this section on a new printed page. */
.new-page {
break-before: page;
}
/* Ask the browser to keep this block together. */
.keep-together {
break-inside: avoid;
}
/* Example: avoid splitting a figure and its caption. */
figure {
break-inside: avoid;
}
These are layout requests, not guarantees: a block taller than the printable area still has to fit somehow, and browsers may handle fragmentation differently. Apply break rules to meaningful sections rather than every element, then inspect where the actual pages split. See MDN’s paged media guide and its fragmentation guide.
5. Add a print button with JavaScript
A button on a page you control can call window.print(). This opens the browser’s print dialog; it does not silently create or download a PDF.
<button id="print" type="button">Print or save as PDF</button>
<script>
document.querySelector("#print").addEventListener("click", () => {
window.print();
});
</script>
The browser waits for document loading to finish before printing if window.print() is called while the document is still loading. That does not guarantee that application-specific asynchronous content, such as data fetched after initial load, has rendered. If your page loads content asynchronously, make sure it is ready before the user clicks. Reference: MDN’s Window.print() reference.
6. Review the preview and the saved PDF
Before sharing or relying on a generated PDF, check these details:
- Clipping and scaling: Is any content cut off or unexpectedly reduced to fit?
- Margins and whitespace: Are the requested margins present, and are there large blank areas?
- Page breaks: Are headings separated from their content? Are tables, figures, or cards split awkwardly?
- Images and fonts: Did important assets finish loading, and are they legible in the PDF?
- Colors and backgrounds: Does the result match the intended design under the selected print settings?
- Links: Are link labels readable without screen-only styling?
- Headers and footers: Check whether the browser adds any and whether its dialog offers controls for them.
Print preview and the PDF are the practical checks for the combination of CSS, browser support, and print settings. The available CSS references do not establish a universal browser preference or a single print-dialog sequence.
7. Troubleshooting print CSS
| Symptom | Likely cause | What to try |
|---|---|---|
| A navigation bar or button still prints | The selector does not match the page element, or another rule overrides it. | Inspect the element and selector, check the print media rules in the cascade, and use a more specific selector if needed. |
| The page is clipped or content is too small | The screen layout has fixed widths, or the page size, margins, scaling, or orientation do not suit the content. | Set print widths to auto, review @page, and check the print dialog’s fit or scaling settings. |
| A section breaks across pages | The content exceeds the printable area, or no fragmentation guidance is applied. | Try break-inside: avoid on a suitably sized block, or start a major section with break-before: page. Verify the result in preview. |
| Page size or orientation appears unchanged | The browser may not support the requested behavior consistently, or dialog settings may override or interact with CSS. | Check the target browser’s preview and settings; test the output in the environment your readers use. |
| Images, charts, or application data are missing | Assets or asynchronous application content may not be ready when printing starts. | Wait for the content your application needs to render before calling window.print(), then inspect the PDF. |
| Background colors are absent | The browser’s print settings may omit backgrounds, or print color behavior may differ. | Review available dialog settings and simplify the print design so essential information does not depend on background color alone. |
| JavaScript does not download a PDF | window.print() opens the print dialog; it is not a direct PDF download API. |
Use the dialog’s PDF output option, or use a PDF generation workflow suited to your application’s requirements. |
8. Or skip the browser setup
If your goal is a screenshot or PDF capture of a URL without configuring a browser, ScreenshotNeo provides a website screenshot API and MCP server. Its API can return PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.
For a PDF response, add format=pdf to the 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
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)
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(({ writeFile }) =>
writeFile('page.pdf', Buffer.from(await res.arrayBuffer()))
);
The Node.js example above needs top-level await support. In a CommonJS script or a context without top-level await, put the request and file write inside an async function. Keep your access key private on a server; do not embed it in public client-side code.
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers indicate the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
9. Performance, reliability, and cost considerations
For a page you control, CSS print styles add no separate screenshot service to the workflow. The main practical work is iterating on layout and checking the output in the browser and settings your readers use. If the document depends on asynchronous content, make its readiness part of the print flow.
A browser-based PDF destination is useful for an individual save, while repeatable captures from code may suit automated workflows. With ScreenshotNeo, the plan determines the monthly included capture volume: Free includes 1,000 per month, Starter is $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 available on every plan. Only clean shots are billed, so bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing.
For either workflow, inspect representative outputs when changing page styles, browser settings, or capture options. CSS specifications and documentation describe controls, but they do not guarantee identical output for every browser and page.
10. Frequently asked questions
Can CSS automatically save a PDF to a visitor’s device?
No. CSS controls print presentation. window.print() opens the browser’s print dialog, where the visitor can choose an available PDF output option.
Can I use a separate stylesheet only for printing?
Yes. Link the stylesheet with media="print", or put print rules in an @media print block in an existing stylesheet.
Why should I inspect the target browser’s preview?
Page dimensions, fragmentation, and print settings can vary in support and behavior. Preview shows how the CSS and that browser’s settings combine for the document.
Can window.print() wait for data loaded by my application?
It waits for document loading when called during that phase, but that alone does not establish that later asynchronous application content has finished rendering. Ensure required content is ready before printing.


