avatar

ShīnChvën ✨

Effective Accelerationism

Powered by Druid

React Web Page as a PDF Template: Pagination, Headers, Footers and More

Tue Mar 07 2023

A web page can be printed to PDF file with browser or puppeteer, so it is quite possible to create a PDF template with React.

CSS Print Media Query

The first thing you need to know is that CSS has a special media query for printing, which is @media print. You can use it to customize the print output.

For example, you can use it to hide some elements when printing or define page height and page margin:

@media print {
  .hide-on-print {
    display: none;
  }

  @page {
    size: A4;
    margin: 0;
  }
}

However, while some media queries associated with @media print, such as @bottom-center, are not supported by browsers, they cannot be used to define PDF page headers and footers on a web page. Nevertheless, it is still possible to handle them using web standard CSS, such as by creating a customized page counter in a custom page header or footer.

Create a React Component to Handle PDF Pagination, Page Header, and Page Footer

Pagination is a common requirement for PDF generation. We can easily create a React component to handle pagination:

import React from 'react';
import styles from './PdfPage.less';

export type PdfPageProps = PropsWithChildren<{
  className?: string;
  style?: React.CSSProperties;
}>

const PdfPage: React.FC<PdfPageProps> = ({
    children,  // Pass page content as children in this component 
    className, 
    style
    }) => {
  return <div className={[styles.page, className].join(' ')} style={style}>

    <div className={styles.header}>place your page header here</div>

    {children}

    <div className={styles.footer}>place your page footer here</div>

  </div>
}

Then we define page layout in CSS:

@page {
  size: A4; // use A4 paper size
  margin: 0 !important; // remove page margin so that we can use the whole page
}

@media print {
  .page {
    padding: 20mm;  // use padding to create page margin
    break-inside: avoid; // avoid page break inside the page
    height: calc(98vh - 40mm); // use height to create page break
  }
}

.page {
  position: relative; // use position relative to place page header and footer
  display: flex;
  flex-direction: column;
}

.header { // place header at the top of the page
  position: absolute;
  right: 0;
  left: 0;
  top: 0;
  width: 100%;
}

.footer { // place footer at the bottom of the page
  position: absolute;
  right: 0;
  left: 0;
  bottom: 0;
  width: 100%;
}

Then we can use it to create a PDF page:

<PdfPage>
  <h1>Page 1</h1>
  <p>Page 1 content</p>
</PdfPage>

It is advisable to ensure that the height of the content on each page is smaller than the height of the page itself. Otherwise, the content may be cut off and continue onto the next page. To prevent this issue, each page should be adjusted manually to ensure that the content fits within the page and does not get cut off.

Print Web Page to PDF File Using Puppeteer

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It can be used to generate PDF files from a web page.

import puppeteer from 'puppeteer';

const webPageToPdf = async (url: string, filepath: string) => {
  const browser = await puppeteer.launch({headless: true});
  const page = await browser.newPage();
  await page.goto(url,
    {waitUntil: 'networkidle0'} // wait until page is fully loaded
  );
  await page.pdf({
    format: 'A4', // use A4 paper size
    path: filepath, // save PDF to a file
    margin: undefined // remove page margin so that we can use the whole page
  });
  await browser.close();
  return filepath;
};

export default webPageToPdf;