Paged.js
Paged.js is an open-source JavaScript polyfill (MIT license) for the CSS Paged Media specification. Add one script tag to your HTML and Paged.js unlocks typographic features that are not available in standard PDF conversion: headers that reflect the current chapter, cross-references for tables of contents, facing-page binding margins, and print marks for professional production.
Setup
Script tag
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script>
Render options
| Option | Value | Reason |
|---|---|---|
render.waitForSelector | ".pagedjs_page" | Wait until Paged.js has finished paginating |
render.mediaType | "screen" | Paged.js manages print layout itself |
layout.preferCSSPageSize | true | Use the page size from your @page CSS rule |
{
"source": "...",
"render": {
"waitForSelector": ".pagedjs_page",
"mediaType": "screen"
},
"layout": {
"preferCSSPageSize": true
},
"timeout": 60
}
Features
Dynamic headers
Static headers with page numbers already work via PolyDoc templates. What Paged.js adds is a header that automatically reflects the current section: as each new chapter begins, the header switches to its title. This is done by extracting text from the document using
string-set. The :first pseudo-class suppresses the header on the cover page./* Capture the nearest preceding h1 into a named string */
h1 { string-set: chapter-title content(); }
@page {
@top-left { content: string(chapter-title); font-size: 9pt; color: #888; }
@top-right { content: "Annual Report 2026"; font-size: 9pt; color: #888; }
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
font-size: 9pt;
}
}
/* No header on the cover page */
@page :first {
@top-left { content: none; }
@top-right { content: none; }
}
Cross-references
target-counter() resolves to the page number of the linked element at render time. This makes it possible to generate a table of contents where page numbers are always accurate, without manually maintaining them. Use case: reports, books, legal documents.<!-- TOC entry links to the target section -->
<ol class="toc">
<li><a href="#ch1">Introduction</a></li>
<li><a href="#ch2">Product Overview</a></li>
</ol>
<!-- Target sections -->
<h1 id="ch1">Introduction</h1>
<h1 id="ch2">Product Overview</h1>
.toc a::after {
content: leader(".") " " target-counter(attr(href url), page);
}
Facing pages
Printed books and reports need a wider margin on the inner (spine) side so content is not lost in the binding. The
:left and :right pseudo-classes let you set asymmetric margins for even and odd pages respectively. Use case: any document intended for double-sided printing or binding.@page :left {
margin-left: 2.5cm; /* inner / spine */
margin-right: 1.5cm; /* outer / edge */
}
@page :right {
margin-left: 1.5cm; /* outer / edge */
margin-right: 2.5cm; /* inner / spine */
}
/* Extra top space on the title page */
@page :first {
margin-top: 6cm;
}
Named pages
Apply a different
@page rule to a specific element so that only those pages switch orientation. Use case: a wide data table or chart inside an otherwise portrait report.@page wide {
size: A4 landscape;
margin: 1.5cm 2cm;
}
.landscape-section {
page: wide;
}
<section class="landscape-section">
<table><!-- wide data table --></table>
</section>
Bleeds & crop marks
A bleed area extends colour and images 3 mm beyond the trim edge so there are no white borders after cutting. Crop marks show the printer where to cut. Use case: brochures, flyers, business cards, any document that goes to a print shop.
@page {
size: A4;
margin: 2cm;
marks: crop cross; /* add crop and registration marks */
bleed: 3mm;
}
.full-bleed-cover {
margin: -3mm;
padding: 3mm;
background: url("cover.jpg") center / cover;
}