Renderpaper
Log in Start free

Page numbers in a PDF from HTML

The usual answer to this is "use Chromium's headerTemplate with the pageNumber class". That answer has a problem and an alternative that is better anyway.

The problem: headerTemplate renders in an isolated context that cannot see your stylesheet, so the number arrives unstyled, at the wrong size, in the wrong font. Inlining your CSS into the template to fix that hits Chromium's size limit for it. Renderpaper has no header options at all, so this route is unavailable here.

The alternative is CSS, it is styled by your own stylesheet, and it works.

CSS margin boxes

@page {
  size: A4;
  margin: 20mm;

  @bottom-center {
    content: "Page " counter(page) " of " counter(pages);
    font: 10px system-ui, sans-serif;
    color: #5b6270;
  }
}

That is the whole recipe. counter(page) is the current page, counter(pages) is the total, and both are maintained by the renderer during pagination.

Verified 2026-08-08. A three-page document rendered through Renderpaper, then read back with pdftotext:

One  Page 1 of 3  Two  Page 2 of 3  Three  Page 3 of 3

The numbers are real text in the PDF, not an image, so they are selectable and searchable.

The margin boxes you can use

Chromium supports the sixteen positional margin boxes. The useful ones:

@page {
  @top-left     { content: "Northwind Trading AB"; }
  @top-right    { content: "Invoice 2026-0142"; }
  @bottom-left  { content: "Confidential"; }
  @bottom-right { content: counter(page); }
}

Each takes content, plus font and colour properties. They do not inherit from body, so set the font explicitly in each — a margin box left unstyled falls back to a serif default that will not match your document.

Different first page

A cover page usually should not be numbered:

@page { @bottom-center { content: "Page " counter(page) " of " counter(pages); } }
@page :first { @bottom-center { content: ""; } }

:first, :left and :right all work, which is enough for the common asymmetric layouts — page numbers on the outer edge of a double-sided document:

@page :left  { @bottom-left  { content: counter(page); } }
@page :right { @bottom-right { content: counter(page); } }

Starting the count somewhere else

If the PDF is a section of a larger document:

@page { counter-reset: page 42; }

What margin boxes cannot do

They take content, not markup. A logo, a table or anything with structure will not go in one. content: url(...) for an image is inconsistent across engines and is not worth relying on.

If you need a structured repeating header — a logo beside an address block — use the table-header-group technique in repeat a header on every page instead, and keep the margin box for the page number. The two compose: the repeating header comes from the document flow, the number from the page margin.

Why not JavaScript

Counting pages in the document is not possible: the page count does not exist until the renderer paginates, which happens after your script has run. Any approach that tries to measure element heights and divide by page height is guessing, and it is wrong as soon as a font substitution changes a line break.

The counters are maintained by the thing doing the pagination. That is the only place the number is actually known.

Last updated 2026-08-08.