§HOW-TO GUIDE · C# · .NET 8 & 10

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

Video walkthrough · 2:56 · Watch on YouTube · Short · 0:56
§STEP BY STEP

Build it step by step.

  1. 1Install the NuGet packages

    SelectPdf.Universal is the library. The native package carries the Chromium engine for one platform: add the one for the platform you run on.

    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.win-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-arm64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.osx-arm64
  2. 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.cs with the code below.

    terminal
    dotnet new console -n PdfSample
    cd PdfSample
  3. 3Build the report

    Program.cs · lines 3–13
    // 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 section with the class chapter, which the CSS uses to start it on a new page.

  4. 4Set the page breaks in CSS

    Program.cs · lines 15–26
    // 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: always on the cover ends the first page after it, and page-break-before: always on 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.

  5. 5Convert and save

    Program.cs · lines 28–32
    HtmlToPdf converter = new HtmlToPdf();
    PdfDocument doc = converter.ConvertHtmlString(html);
    Console.WriteLine($"{doc.Pages.Count} pages");
    doc.Save("report.pdf");
    doc.Close();

    ConvertHtmlString renders the HTML and applies the breaks. doc.Pages.Count prints 4: the cover and one page per chapter. Save writes report.pdf and Close releases the document.

  6. 6Run it

    terminal
    dotnet run

    The program writes report.pdf to the working folder. Without a license key, every page carries a trial watermark; set GlobalProperties.LicenseKey before the first conversion to remove it.

§COMPLETE PROGRAM

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.

Program.cs
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

§OUTPUT

The PDF it produces.

First page of report.pdf, the PDF this sample produces
report.pdf, page 1

Download report.pdf ↓ the file this sample produced, unedited

§VARIATIONS

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.

KeepTogether.cs
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.

PrintStylesheet.cs
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.

RepeatTableHeaders.cs
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();
    }
}
§TROUBLESHOOTING

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

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
?FAQ

Questions, answered.

Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.

Add page-break-before: always to the element that should start a new page, or page-break-after: always to the element that should end one. SelectPdf.Universal reads these CSS rules when it converts the HTML.
Set page-break-inside: avoid on it. The converter keeps its content on one page if possible, and moves it to the next page when it does not fit.
No. The converter pages the content automatically, and you change where the breaks fall with CSS in the page.
Yes. Set converter.Options.CssMediaType to HtmlToPdfCssMediaType.Print and the page is rendered with its @media print styles, including their page-break rules.