Control page breaks in HTML to PDF with CSS
SelectPdf.Universal pages the converted content automatically. To choose where a page ends, use CSS in the HTML: page-break-before and page-break-after force a break at an element, and page-break-inside: avoid keeps an element on one page.
Compiled and run: Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64
Build it step by step.
-
1Install the NuGet packages
SelectPdf.Universalis the library. The native package carries the Chromium engine for one platform: add the one for the platform you run on.dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.win-x64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.linux-x64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.linux-arm64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.osx-arm64
-
2Create a console app
Any .NET 8 or .NET 10 project works. To follow along, create a console app, run the install commands above inside its folder, then replace
Program.cswith the code below.dotnet new console -n PdfSample cd PdfSample
-
3Build the report
// a report: a cover page and one chapter per region string chapters = ""; foreach (string region in new[] { "North", "South", "West" }) { chapters += $""" <section class="chapter"> <h2>{region} region</h2> <p>Sales, returns and new customers for the {region} region.</p> </section> """; }A report with one chapter per region. Each chapter is a
sectionwith the classchapter, which the CSS uses to start it on a new page. -
4Set the page breaks in CSS
// the page breaks come from the page's CSS string html = $$""" <style> .cover { page-break-after: always; } .chapter { page-break-before: always; } </style> <div class="cover"> <h1>Annual report 2026</h1> <p>Contoso Ltd</p> </div> {{chapters}} """;page-break-after: alwayson the cover ends the first page after it, andpage-break-before: alwayson every chapter starts it on a new page. The converter reads these rules from the HTML; there is no page-break option to set in code. -
5Convert and save
HtmlToPdf converter = new HtmlToPdf(); PdfDocument doc = converter.ConvertHtmlString(html); Console.WriteLine($"{doc.Pages.Count} pages"); doc.Save("report.pdf"); doc.Close();ConvertHtmlStringrenders the HTML and applies the breaks.doc.Pages.Countprints 4: the cover and one page per chapter.Savewritesreport.pdfandClosereleases the document. -
6Run it
dotnet run
The program writes
report.pdfto the working folder. Without a license key, every page carries a trial watermark; setGlobalProperties.LicenseKeybefore the first conversion to remove it.
The complete Program.cs.
This is the exact file our samples pipeline compiled and ran on Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.
using SelectPdf.Universal;
// a report: a cover page and one chapter per region
string chapters = "";
foreach (string region in new[] { "North", "South", "West" })
{
chapters += $"""
<section class="chapter">
<h2>{region} region</h2>
<p>Sales, returns and new customers for the {region} region.</p>
</section>
""";
}
// the page breaks come from the page's CSS
string html = $$"""
<style>
.cover { page-break-after: always; }
.chapter { page-break-before: always; }
</style>
<div class="cover">
<h1>Annual report 2026</h1>
<p>Contoso Ltd</p>
</div>
{{chapters}}
""";
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertHtmlString(html);
Console.WriteLine($"{doc.Pages.Count} pages");
doc.Save("report.pdf");
doc.Close();
Open the project on GitHub → this program and the variations below, ready to dotnet run
The PDF it produces.
Download report.pdf ↓ the file this sample produced, unedited
Variations you will need next.
Each variation is a separate file in the same project, compiled and run with the program above.
Keep a block together on one page
page-break-inside: avoid on a block keeps its content on one page if possible. Here eight tall cards fill two pages, four on each: the fifth card does not fit at the bottom of the first page, so it starts the second one instead of being split.
using SelectPdf.Universal;
static class KeepTogether
{
public static void Run()
{
// tall blocks: without a rule, some of them would be split across two pages
string cards = "";
for (int i = 1; i <= 8; i++)
cards += $"""<div class="card"><h3>Customer {i}</h3><p>Account summary for customer {i}.</p></div>""";
// page-break-inside: avoid moves a block that does not fit to the next page
string html = $$"""
<style>
.card { height: 300px; border: 1px solid #999; margin-bottom: 20px; page-break-inside: avoid; }
</style>
{{cards}}
""";
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertHtmlString(html);
doc.Save("customers.pdf");
doc.Close();
}
}
Page breaks only in the print stylesheet
Many pages keep print rules in @media print, so they read normally in a browser. The converter uses the screen styles by default; Options.CssMediaType = HtmlToPdfCssMediaType.Print renders with the print styles, and each h2 starts a new page.
using SelectPdf.Universal;
static class PrintStylesheet
{
public static void Run()
{
// page breaks only in the print stylesheet, so the page reads normally on screen
string html = """
<style>
@media print { h2 { page-break-before: always; } }
</style>
<h1>User guide</h1>
<h2>Installation</h2><p>Install the package.</p>
<h2>Configuration</h2><p>Set the options.</p>
""";
// render with the @media print styles instead of the screen styles
HtmlToPdf converter = new HtmlToPdf();
converter.Options.CssMediaType = HtmlToPdfCssMediaType.Print;
PdfDocument doc = converter.ConvertHtmlString(html);
doc.Save("user-guide.pdf");
doc.Close();
}
}
Repeat table headers on every page
A long table splits over several pages. display: table-header-group on the thead repeats the header row at the top of each page that holds part of the table; set Options.CssMediaType = HtmlToPdfCssMediaType.Print as well, since the header repeats when the page is rendered with the print media type.
using System.Text;
using SelectPdf.Universal;
static class RepeatTableHeaders
{
public static void Run()
{
// a table long enough to run over several pages
StringBuilder rows = new StringBuilder();
for (int i = 1; i <= 120; i++)
rows.Append($"<tr><td>Order {1000 + i}</td><td>${i * 25}</td></tr>");
string html = $"""
<table style="width: 100%; border-collapse: collapse;">
<thead style="display: table-header-group;">
<tr><th align="left">Order</th><th align="left">Amount</th></tr>
</thead>
<tbody>{rows}</tbody>
</table>
""";
// the header row repeats at the top of every page with the print media type
HtmlToPdf converter = new HtmlToPdf();
converter.Options.CssMediaType = HtmlToPdfCssMediaType.Print;
PdfDocument doc = converter.ConvertHtmlString(html);
doc.Save("orders.pdf");
doc.Close();
}
}
Common problems and fixes.
Breaks in @media print are ignored
The converter uses the screen styles by default. Set converter.Options.CssMediaType = HtmlToPdfCssMediaType.Print to render with the print stylesheet, as the second variation does.
A block is still split across two pages
page-break-inside: avoid keeps a block together only if possible: a block taller than a page is still split.
There is no page-break property on the converter
Page breaks come from the page's CSS, not from converter properties. Add the rules to the HTML you convert, in a style element or a stylesheet.
The break falls on the wrong side of an element
page-break-before puts the element at the top of a new page; page-break-after makes the next content start a new page. Put the rule on the element that should start or end the page.
Good to know.
- By default the converter pages the content automatically, and the rendering engine decides where the page breaks fall. Source
- Pagination is customized from the page's own CSS rather than from converter properties. Source
- page-break-before and page-break-after insert a page break before or after an element, and the element can be anything: an image, a table, a table row, a div or text. Source
- page-break-inside: avoid keeps the content of an element from being split into several pages, if possible. Source
- The CssMediaType option renders the page with its @media screen styles by default, or with its @media print styles when set to Print. Source
Questions, answered.
Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.