paged.js notes

  1. What is paged.js?
    1. The differences between paged.js and paged.polyfill.js:
      1. What actually happens in practice
      2. paged.js (with the Preview API)
      3. In plain language
  2. Caveats
    1. 1. A server is required
    2. 2. The preview does not work correctly when the source element is hidden
    3. 3. Headers/Footers are not added to each page
    4. Page number is always 2 on all the pages
    5. Paged.js preview error: Error: item doesn’t belong to list

What is paged.js?

paged.js is a javascript library to transform HTML document into print-ready pdf.

paged.js comes as two different scripts - paged.js and paged.polyfill.js. There is also a commend line version which can be used to generate a PDF using a headless browser.

The differences between paged.js and paged.polyfill.js:

File What it does How it works When to use it
paged.polyfill.js Acts like a print-layout engine for the whole webpage Searches the entire document, moves the content, paginates it, and replaces your DOM with paged output When you’re converting your whole page to paginated output (e.g., print / PDF export)
paged.js (with import { Previewer } from "pagedjs") You control what to paginate and where to output You give it a specific DOM node as input, and another node as output, and it inserts the paginated pages there When you want to paginate only part of the page, or use Paged.js inside React / Vue / Svelte

What actually happens in practice

paged.polyfill.js

  • Automatically waits for the document to load
  • Finds everything in <body>
  • Moves it into a hidden template
  • Generates page boxes + flows
  • Rewrites the document with pages in place

Think of it like printing your webpage in the browser, but beautifully.

Pros:

  • Headers/footers (position: running()) work automatically
  • No manual JavaScript setup
  • Good for “convert my whole HTML doc to print layout”

Cons:

  • Hard to use in React (because it rewrites the DOM)
  • You can’t choose which div to paginate
  • Hard to preview in one place and edit in another

paged.js (with the Preview API)

You use:

1
2
3
4
import { Previewer } from "pagedjs";

const paged = new Previewer();
paged.preview(sourceElement, [cssFiles], previewContainer);

This lets you:

  • Keep your HTML exactly where it is
  • Paginate only specific content
  • Render the pages into a separate container

Pros:

  • Perfect for React / SPA / dynamic content
  • Source stays editable
  • Output is controlled and isolated

Cons:

  • Running headers/footers do not auto-work unless:
    • You pass CSS explicitly
    • The running header element is inside the paginated source

In plain language

paged.polyfill.js

“I’ll print your entire document as a book. Stand back.”

paged.js + Previewer()

“Tell me which content to convert, and where to output the pages.”

Caveats

1. A server is required

If you open your html file with paged.js embed in the file:// protocal, the page will be blank. There are error messages on the console panel, sush as

1
Access to XMLHttpRequest at 'file://<path-to-your-file>/example.css' from origin 'null' has been blocked by CORS policy

The reason for the error is that paged.js has to work with a web server so that it can fetch CSS file content used in your html file by using XMLHttpRequest API and apply it to the generated pages.

2. The preview does not work correctly when the source element is hidden

When we use paged.js with a hidden source element (e.g. display:none;), only some of the pages will be generated on the target container. Besides, all the page content will be hidden except on the first page.

You may want to hide the source container with a collapse height:

1
2
3
4
.collapsed {
max-height: 0;
overflow: hidden;
}

Other ways like display: none;, opacity: 0 or position: aboslute: do not work.

3. Headers/Footers are not added to each page

If you are using paged.js, make sure the headers/footer are inside the source element, otherwise they won’t be rendered in pages.

What’s more, remember to check if the CSS file are included when the preview() function is called.

1
2
3
4
5
6
7
8
import { Previewer } from "pagedjs";

const paged = new Previewer();
paged.preview(
document.querySelector("#source"),
["path/to/css/file.css"], // <=important
document.querySelector("#target")
);

Page number is always 2 on all the pages

The can happen when the CSS files are included in the head tag and then included again with the preview() function is called.

Consider removing duplicated CSS file from the head tag and only load them with the preview() function call.

Paged.js preview error: Error: item doesn’t belong to list

This error can occur when the Previewer object is reused to perform preivew multiple times. This is a bug reported in 2024 and hasn’t been fixed.

We need to create a new Previewer object every time when we need to call the preview() function to work around this issue.