npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

declarative-pdf

v1.1.0

Published

A tool for generating PDF documents from declarative HTML templates

Downloads

144

Readme

Declarative PDF visualization

Table of contents

Features

  • repeating sections - customizable headers, footers, and backgrounds for each page
  • section variants - define variants for headers, footers, and backgrounds for different pages
  • page size and orientation - define page size by name or by width, height and ppi
  • multi-page content - content that spans across multiple pages
  • page numbering - display page numbers within headers, footers or backgrounds

Motivation

Creating PDF documents programmatically can be a complex and time-consuming task. Traditional methods often require intricate control over layout and content, which can be cumbersome and error-prone. This tool aims to simplify this process by using declarative HTML elements to control layout and content. By leveraging puppeteer, it slices your template and generates PDF elements from it, which are then used to assemble the final PDF document.

[!NOTE] This approach allows you to focus on the content of your PDF, rather than the layout, making PDF generation more intuitive and efficient.

Installation

Install it locally in your project folder:

# using npm
npm install --save declarative-pdf
# or using yarn
yarn add declarative-pdf
# or using pnpm
pnpm add declarative-pdf

[!NOTE] This package supports both CommonJS and ES modules. So you can either require it or import it.

Usage examples

Create from HTML template string

// ./docs/examples/example-a.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';

(async () => {
  const html = await read('example-simple.html');
  const browser = await puppeteer.launch();

  const pdf = new DeclarativePDF(browser);
  const pdfBuffer = await pdf.generate(html);
  await write('example-a.pdf', pdfBuffer);

  await browser.close();
})();

Most basic example is to create a PDF from a string. This approach is useful when you want to let declarative-pdf handle opening and closing tabs, and you don't need to interact with the page directly.

Create from Puppeteer tab

// ./docs/examples/example-b.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';

(async () => {
  const html = await read('example-simple.html');
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setContent(html);

  const pdf = new DeclarativePDF(browser);
  const pdfBuffer = await pdf.generate(page);
  await write('example-b.pdf', pdfBuffer);

  await browser.close();
})();

Another way is when you already have a puppeteer tab open, and you want to generate a PDF from it. This approach is useful when you want to interact with the page directly, or when you want to use puppeteer's API to manipulate the page before generating a PDF.

Debug created PDF

// ./docs/examples/example-c.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';

(async () => {
  const html = await read('example-simple-bgcolor.html');
  const browser = await puppeteer.launch();

  const pdf = new DeclarativePDF(browser, {debug: {timeLog: true, attachSegments: true, pdfName: 'example-c.pdf'}});
  const pdfBuffer = await pdf.generate(html);
  await write('example-c.pdf', pdfBuffer);

  await browser.close();
})();

Building a complex template can lead to some unexpected results. CSS styles might not be applied as expected, or the layout might not be as you intended. Some backgrounds might interfere with the content, or headers and footers might not be positioned correctly.

To help you debug these issues, you can set the debug option when creating a new DeclarativePDF instance. This will attach individual sections from which the final PDF is constructed, as well as the timing logs for each step.

[!NOTE] Only some PDF readers can view attachments. This includes Adobe Acrobat Reader, Foxit Reader, and Firefox browser.

Example C attachments location

Complete examples

// ./docs/examples/mockup-pages.js
import puppeteer from 'puppeteer';
import DeclarativePDF from '../../dist/index.js';
import {read, write} from './utils.js';

(async () => {
  const browser = await puppeteer.launch();
  const pdf = new DeclarativePDF(browser);

  for (const name of ['a4-72-multipage', 'a4-72-standard', 'a4-297-standard']) {
    const html = await read(`${name}.html`);
    const pdfBuffer = await pdf.generate(html);

    await write(`${name}.pdf`, pdfBuffer);
  }

  await browser.close();
})();

Create from Express server

It is quite possible to plug this into your express server. For example, if you send POST request with template as a string, this would respond with generated PDF file.

[!NOTE] Spinning up puppeteer browser can be an expensive operation, so it might be beneficial to keep browser instance running. DeclarativePDF will open and close tabs, but not the browser itself.

const express = require('express');
const DeclarativePDF = require('declarative-pdf');
const puppeteer = require('puppeteer');

(async function () {
  const app = express();
  const browser = await puppeteer.launch();

  app.use(
    express.urlencoded({
      extended: true,
      limit: '2000kb', // default limit is 100kb and templates can grow
    })
  );

  async function generate(req, res) {
    const template = req.body.template;
    const name = req.body.name;
    const filename = `${name}.pdf`;

    const pdfBuffer = await new DeclarativePDF(browser).generate(template);

    res.setHeader('Content-disposition', `inline; name="${name}"; filename="${filename}"`);
    res.setHeader('Content-Type', 'application/pdf');
    res.writeHead(200);
    res.end(Buffer.from(pdfBuffer).toString('base64'));
  }

  app.post('/generate', generate);

  const server = http.createServer(app);
  server.listen(80);
})();

Template

Template structure:

<html>
  <head>
    <style>
      /* Add any CSS styles here */
    </style>
  </head>
  <body>
    <document-page>
      <page-background> /* Add any page-background content here */ </page-background>
      <page-header> /* Add any page-header content here */ </page-header>
      <page-body> /* Add any page-body content here */ </page-body>
      <page-footer> /* Add any page-footer content here */ </page-footer>
    </document-page>
  </body>
</html>

This is the most basic template structure. Check out the template documentation for more advanced features.

Documentation

Further reading:

License

MIT