How to Add a Print Link to an HTML Page
Add a reliable Print this page control with window.print(), print CSS, page breaks, troubleshooting, and a backend screenshot option.
Direct answer: add a semantic link or button, intercept its click, and call window.print(). Use @media print (or a print-only stylesheet) to hide the control and adjust the document for paper or PDF.
<a href="#" id="print-link">Print this page</a>
<script>
document.getElementById('print-link').addEventListener('click', (event) => {
event.preventDefault();
window.print();
});
</script>
window.print() opens the browser’s print dialog for the current document. It does not send anything directly to a printer; the user chooses a printer or “Save as PDF”. See MDN’s window.print() documentation.
1. Choose the right control
Use a <button type="button"> when the control performs an action. Use an anchor when the surrounding design requires a text link; prevent its default navigation as shown above.
<button type="button" id="print-button">Print this page</button>
<script>
document.getElementById('print-button').addEventListener('click', () => window.print());
</script>
Keep the control keyboard reachable, give it visible text, and do not use a submit button inside a form unless you explicitly set type="button".
2. Add print-only CSS
Print styles belong in an @media print block or a stylesheet loaded with media="print". MDN documents this media rule for changing presentation on paper or in a PDF, and the W3C media specification defines print as a media type.
<link rel="stylesheet" href="print.css" media="print">
@media print {
#print-link,
#print-button,
nav,
.screen-only,
.ads,
.chat-widget {
display: none !important;
}
body {
color: #000;
background: #fff;
}
a {
color: inherit;
text-decoration: none;
}
}
Hide every screen-only element that would waste paper. Avoid hiding content that carries meaning. If links must remain useful on paper, append their destinations with generated content:
@media print {
article a[href^="http"]::after {
content: " (" attr(href) ")";
font-size: 0.85em;
overflow-wrap: anywhere;
}
}
3. Set page geometry and page breaks
The @page at-rule controls paper size, orientation, and margins. Explicit breaks help long documents start each chapter cleanly.
@page {
size: A4 portrait;
margin: 1.5cm;
}
@media print {
.chapter {
break-after: page;
}
h1, h2, h3 {
break-after: avoid;
}
table, figure, pre, blockquote {
break-inside: avoid;
}
}
Use break-before: page for a section that must begin on a new sheet, and break-after: page for a section that ends one. Browser support for modern @page is broad in current devices, but older browsers can vary; always inspect print preview.
4. Handle dynamic content around printing
Prefer CSS for ordinary visibility and layout changes. The beforeprint and afterprint events are useful when content itself must change temporarily, such as replacing an interactive chart with a static summary.
window.addEventListener('beforeprint', () => {
document.body.classList.add('is-printing');
});
window.addEventListener('afterprint', () => {
document.body.classList.remove('is-printing');
});
@media print {
.interactive-chart { display: none; }
.print-chart { display: block; }
}
.print-chart { display: none; }
Wait for fonts, images, and data to finish loading before the user prints. If your app renders asynchronously, disable the print control until the printable content is ready or show a clear loading state.
5. Complete runnable HTML example
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Project report</title>
<style>
body { font: 16px/1.5 system-ui, sans-serif; margin: 2rem auto; max-width: 70ch; }
.toolbar { display: flex; gap: .75rem; margin-bottom: 2rem; }
@page { size: A4 portrait; margin: 1.5cm; }
@media print {
.toolbar, nav, .screen-only { display: none !important; }
body { max-width: none; margin: 0; color: #000; background: #fff; }
.chapter { break-after: page; }
h2 { break-after: avoid; }
}
</style>
</head>
<body>
<nav class="screen-only">Site navigation</nav>
<main>
<div class="toolbar">
<button type="button" id="print-button">Print this page</button>
</div>
<article>
<h1>Project report</h1>
<section class="chapter">
<h2>Summary</h2>
<p>This content remains in the printed document.</p>
</section>
<section>
<h2>Details</h2>
<p>The second section starts after the configured page break.</p>
</section>
</article>
</main>
<script>
document.getElementById('print-button').addEventListener('click', () => window.print());
</script>
</body>
</html>
6. Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
| Click navigates to the top or changes the URL | An anchor’s default action was not cancelled | Call event.preventDefault(), or use a button. |
| Print control appears on paper | No print rule, wrong selector, or stronger rule wins | Target the actual ID/class and use display:none !important when needed. |
| Colors or backgrounds are missing | Browser print settings may disable background graphics | Provide high-contrast text and tell users to enable background printing when those colors carry meaning. |
| Content is clipped | Fixed heights, overflow, or viewport-only layout | Remove fixed heights and set print overrides such as overflow: visible and max-width: none. |
| Unexpected blank pages | Forced breaks, large margins, or an element taller than the page | Inspect break-* rules and reduce oversized containers. |
| Heading is separated from its paragraph | Pagination split a block | Use break-after: avoid on headings and break-inside: avoid on small grouped blocks. |
| Late data is absent | Printing started before rendering completed | Gate the button on readiness, await data, and verify in print preview. |
| Script errors on pages without the control | getElementById returned null |
Check for the element before adding the listener, or load the script only on pages that include it. |
7. Performance, reliability, and cost
- Performance:
window.print()reuses the current page, so the main cost is layout and pagination. Keep print CSS simple and avoid expensive animations or continuously updating components. - Reliability: Test Chromium, Firefox, and Safari print previews, plus narrow and wide documents. Check keyboard activation, page breaks, missing images, fonts, and right-to-left or long unbroken text.
- Cost: The browser API itself has no service fee. Users may incur ordinary printer or paper costs; saving as PDF stays local to the browser.
8. Or skip the browser setup
If you need a screenshot or PDF of the finished page in a backend job, ScreenshotNeo provides a GET endpoint. Its capture options include full-page output, PDF paper settings, custom CSS and JavaScript, waiting for selectors or network idle, and element capture.
See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/printable-page -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/printable-page"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/printable-page' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
9. FAQ
Can I print without JavaScript?
Yes, the browser’s print command and print CSS still work. JavaScript only adds a convenient in-page control.
Should the control be a link or button?
Use a button for an action. Use a link when your visual language requires one, and cancel its default navigation.
How do users print only one element?
Give that element a print class, hide unrelated regions in @media print, and remove fixed screen layout constraints.
Where do users choose PDF?
In the browser print dialog, select the operating system’s “Save as PDF” destination when available.


