window.print()
function to open the default browser printing prompt, which provides a “print to pdf” option. It is also possible to customize the print view by adding specific CSS that applies only to printing (using the "print" media query and print-related CSS properties). This API is an excellent and simple method for printing the current page, but it doesn’t provide a clean way to export a specific UI piece. Besides, there are many cross-browser incompatibility issues with respect to printing, so we decided to try another solution.// ./ssr/src/index.js
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import MyFancyReport from './components/MyFancyReport';
export function renderStaticReport(reportProps) {
const reportMarkup = ReactDOMServer.renderToStaticMarkup(
<MyFancyReport {...reportProps} />
);
return `
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
${reportMarkup}
</body>
</html>
`;
}
ReactDomServer.renderToString()
function returns an HTML string for the initial state of the component. In this example, we used ReactDomServer.renderToStaticMarkup()
, which also strips out all of the extra DOM attributes that are used internally by React.Document
, Window
, Navigator
, and LocalStorage
. These are not part of the Node.js runtime, and you will either have to check if they are defined and handle logic accordingly, or add a new entry point for your SSR bundle to polyfill them.ReactDomServer.renderToStaticMarkup()
is a synchronous single-pass render function, which means that some hooks won’t apply, including useEffect
and useState
, same for lifecycle methods like componentDidMount
and componentDidUpdate
. It is possible, however, to use the component constructor and the componentWillMount
method.// ./webpack.config.js
const path = require('path');
const webpack = require('webpack');
// existing config
const clientWebpackConfig = {
...
};
// new SSR config
const ssrWebpackConfig = {
...clientWebpackConfig,
entry: './ssr/src/index.js',
target: 'node',
output: {
path: path.resolve(__dirname, 'ssr/lib'),
filename: 'ssrService.js',
libraryTarget: 'commonjs2',
},
};
module.exports = [clientWebpackConfig, ssrWebpackConfig];
npm install puppeteer
// ./services/pdfService.js
const puppeteer = require('puppeteer');
const ssrService = require('../ssr/lib/ssrService.js');
const MY_PDF_PORTION = {
width: 1766,
height: 1000,
};
module.exports.generatePdfFromHtml = async (htmlStr) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport(MY_PDF_PORTION);
await page.setContent(htmlStr, { waitUntil: 'networkidle0' });
const pdfBuffer = await page.pdf({ printBackground: true });
await browser.close();
return pdfBuffer;
};
networkidle0
to make sure that dynamic resources will be available when the PDF is being generated. Also, keep in mind that there are many options for customizing the PDF.