Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Put a forced break on a block-level element that remains in normal document flow, then render the HTML with KnpSnappy. The most reusable pattern is:

<div class="page-break"></div>
<section class="next-section">...</section>
@media print {
  .page-break {
    page-break-before: always;
  }
}

You can instead place page-break-before: always on the next section or page-break-after: always on the preceding block. Because KnpSnappy delegates PDF rendering to wkhtmltopdf, the generated PDF—not the browser preview—is the authority.

How page breaks work in KnpSnappy

KnpSnappy is a PHP integration around wkhtmltopdf. Your application supplies HTML and CSS; wkhtmltopdf lays that document out as paged media and writes the PDF. CSS page-break properties therefore belong in the HTML template used for the PDF, not in a separate KnpSnappy option.

CSS 2.2 defines page-break-before, page-break-after, and page-break-inside for paged output. A value of always forces a break at the relevant margin. A value of avoid expresses a preference, not an unlimited guarantee.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Force a new page before a section

Reusable break element

This is useful when several templates need the same behavior:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @media print {
      .page-break {
        page-break-before: always;
      }
    }
  </style>
</head>
<body>
  <section>
    <h1>Executive summary</h1>
    <p>Content that belongs on the first page.</p>
  </section>

  <div class="page-break"></div>

  <section>
    <h1>Detailed results</h1>
    <p>This heading starts on the next PDF page.</p>
  </section>
</body>
</html>

The empty element must be in normal flow. Do not position it absolutely, put it inside a transformed container, or rely on a browser-only layout effect.

Break on the section itself

@media print {
  .next-section {
    page-break-before: always;
  }
}

This avoids an extra element and makes the intent explicit in the section’s class. The equivalent “after” form is:

@media print {
  .summary {
    page-break-after: always;
  }
}

Choose one location per boundary. Applying both to adjacent elements can make the template harder to reason about and may create an unintended blank page when surrounding rules also force breaks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep headings, cards, and tables together

Heading with the following content

Authors commonly try page-break-after: avoid on a heading. It can help, but wkhtmltopdf may still move the heading when the following content cannot fit. A practical pattern is to wrap the heading and the first block of its content:

@media print {
  .heading-group {
    page-break-inside: avoid;
  }
}

Keep the group small enough to fit on a page. A heading followed by a multi-page table cannot be made indivisible.

Prevent a block from splitting

@media print {
  .invoice-card,
  .signature-block {
    page-break-inside: avoid;
  }
}

avoid is a constraint the layout engine tries to satisfy. CSS also allows the renderer to relax break constraints when honoring them would leave content overflowing a page. Very tall content, long code listings, and oversized table rows may therefore split despite this rule.

Tables and repeated headers

Use a semantic <thead> and <tbody>, and test the actual PDF. A row that is taller than the printable area cannot remain intact. Avoid putting a whole table inside a floated wrapper, and be cautious with nested tables, which can create additional pagination edge cases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the CSS reach the PDF renderer

  1. Use print-specific rules. Put the rule in an inline <style> block or an external stylesheet that the PDF HTML can load. @media print is clear and prevents the rule from changing the screen version.
  2. Confirm the generated HTML contains the class. A conditional template branch, missing variable, or sanitization step can remove the break element before wkhtmltopdf sees it.
  3. Use block-level boxes. A break on an inline element is unreliable. Use a div, section, or another block-level element.
  4. Keep it in normal flow. Floats, absolute positioning, fixed positioning, and complex transforms can change where the renderer sees a margin.
  5. Render and inspect the PDF. A successful browser preview only proves that the screen layout looks right; it does not prove that wkhtmltopdf applied the paged-media rule.

Why page-break-before may be ignored

A floated ancestor

A documented wkhtmltopdf issue reported ignored breaks when an outer parent had float: left. The reported workaround was to disable that float for PDF output:

@media print {
  .pdf-column,
  .pdf-column * {
    float: none;
  }
}

This is a workaround for that layout pattern, not a guarantee for every template. Remove only the float that prevents pagination, then check widths and margins again.

An ancestor prevents legal break points

Inspect parents for page-break-inside: avoid. If a large wrapper is marked unbreakable, the renderer may have no acceptable place to honor a child’s forced break. Apply the rule narrowly rather than to the entire document.

The break is outside the content flow

A zero-height marker inside an absolutely positioned header, a flex arrangement that wkhtmltopdf lays out differently, or a marker hidden with layout-affecting CSS can produce no visible break. Move the marker between ordinary block elements and add a temporary border or text label to verify its location.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CSS or assets are not available

Check stylesheet paths, permissions, and any restrictions on local files or remote resources. If the PDF is generated in a worker, the worker may have a different working directory or network access than the web request.

A repeatable troubleshooting sequence

  1. Run the exact wkhtmltopdf binary used in production and record its version with wkhtmltopdf --version.
  2. Confirm the deployed KnpSnappy package and the configured binary path. Do not assume every installation uses the same build.
  3. Replace the target section temporarily with a visible marker such as <div class="page-break">BREAK HERE</div>.
  4. Apply page-break-before: always to that marker or to the next block, not to an inline child.
  5. Disable a floated parent with PDF-specific float: none if the break is ignored.
  6. Search ancestors for page-break-inside: avoid, fixed heights, overflow clipping, absolute positioning, or transforms.
  7. Generate a fresh PDF after every change and inspect page boundaries, blank pages, clipped content, and repeated headers.
  8. Remove the marker and restore the narrowest production rule that works.

Renderer version and maintenance considerations

The official wkhtmltopdf downloads page identifies the 0.12.6 series as stable, released June 11, 2020. Its repository displays an archive notice and is read-only. That does not mean your server runs 0.12.6: package managers, operating-system images, and vendor binaries can differ. Record the actual binary, operating system, fonts, and command-line options when reproducing a pagination problem.

Paper size, margins, orientation, and related page options affect where a break lands. A rule that appears correct on A4 with one margin can produce a different page count on Letter or with a larger header. Keep those options fixed in development and production, and test representative documents rather than a nearly empty sample.

Performance, reliability, and operational checks

  • Use deterministic input. Wait until required data is present before invoking KnpSnappy; missing content can change the page boundary.
  • Control fonts and images. A late-loading web font or an image with unknown dimensions can reflow the document and move a break.
  • Keep templates modular. A reusable page-break class is easier to audit than inline declarations scattered through conditional branches.
  • Test edge sizes. Include a one-page document, a document with a break near the bottom margin, a long table, and a block taller than one page.
  • Log failures. Capture the renderer exit status and stderr so a missing binary, resource error, or timeout is not mistaken for a CSS problem.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need screenshots or PDFs of web pages rather than a PHP template rendered by wkhtmltopdf, ScreenshotNeo provides a one-call API. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For PDF output, set the paper size, margins, orientation, and page ranges in the request. It also supports custom CSS and JavaScript, waiting for a selector, delay, or network idle, clicking an element, hiding selectors, custom headers and cookies, blocking requests or resource types, timezone and geolocation, and asynchronous jobs with signed webhooks. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" 
  -d access_key=YOUR_API_KEY 
  --data-urlencode url=https://stripe.com 
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo documentation for the complete option names and PDF request parameters. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Sign up free to try it.

Common symptoms and fixes

Symptom Likely cause Fix
No break at all Inline or out-of-flow marker; CSS not loaded Use a block in normal flow and verify the PDF HTML and stylesheet.
Break works in one template only Different ancestor layout or missing class Compare computed PDF styles and inspect floats, heights, and overflow.
Unexpected blank page Adjacent forced breaks or a break near a hard page boundary Keep one break rule per boundary and remove redundant markers.
Heading separated from text avoid applied too broadly or insufficient room Wrap the heading with a small following block and use page-break-inside: avoid.
Large block still splits It cannot fit in the printable area Shorten or redesign the block; avoid is not an overflow guarantee.

Frequently Asked Questions

Should I use page-break-before or page-break-after?

Both force the same boundary when applied to adjacent blocks. Use the property that best matches your template ownership: before on the section that must start a page, or after on the section that ends the previous page.

Can KnpSnappy guarantee that a table row never splits?

No. page-break-inside: avoid is a preference constrained by available page space and renderer behavior; a row taller than the printable area cannot fit intact.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does the browser preview disagree with the PDF?

The preview uses the browser’s layout engine, while KnpSnappy invokes your installed wkhtmltopdf binary. Verify the generated PDF and the binary version used by the application.

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.