Skip to main content

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

OptionValueReason
render.waitForSelector".pagedjs_page"Wait until Paged.js has finished paginating
render.mediaType"screen"Paged.js manages print layout itself
layout.preferCSSPageSizetrueUse 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;
}

Tips