PDF generation with React Componenets using Next.js at Server Side

By Brice Séraphin via Dribbble

Next.js is a very popular React framework which supports Server Side Rendering. With SSR, your Next.js app can handle API calls at server side and construct your page HTML using your react components. Besides HTML, could we return something else from the web server? Say… a PDF file?

In my previous project, I successfully generated PDF document on the fly in a Next.js app. The PDF’s content is constructed using react components, so you can still reuse your react components’s code! In this tutorial, I am going to show you the minimal setup needed for PDF generated with a working example. You can find the source code at the end of this tutorial.


Let’s Begin!

I am using the npm package html-pdf for pdf generation. It uses PhantomJS as the underlying engine to render the html and export to PDF. Therefore, the next thing we need is to find a way to convert react components to HTML markup.

Convert react component to HTML markup

The package react-dom already provide us a handy way to parse a react element to HTML markup, and it is just 1 line of code. Sweet!

import { renderToStaticMarkup } from 'react-dom/server';
...
const html = renderToStaticMarkup(component);
You can pass props to your component so it’s content can be dynamic.

With the html markup string ready, we can pass it back to html-pdf and get the pdf buffer easily:

import pdf from 'html-pdf';
...
pdf.create(html, options).toBuffer((err, buffer) => {
// use the pdf buffer
})

I have put the above logic to a helper module which takes a react element and return a promise with pdf buffer. Here is how it looks:

lib/pdfHelper.js

getInitialProps() to connects everything

If you are familiar with Next.js, adding a new route in Next.js is easy. We just have to put a react component in the pages folder in the application root, for example, index.js. Then you can access the page via http://localhost:3000/. The page level components can implement the static method getInitialProps() and it is the place where we can do something server side.

Once we’ve got the pdf buffer in getInitialProps, we can see how to return it to the browser. This static method takes a context argument, which you can also access to the req and res object. With proper response headers and calling res.end(buffer), we will be able to send the pdf file to the browser.

pages/index.js

Notes

  1. Please note that html-pdf can only work in server side. If you try to generate the PDF in client side, you will probably receive this error:
Module not found: Error: Can't resolve 'child_process' in ...

To prevent this error, remember to add the following section in your package.json file. This is to ensure webpack do not compile html-pdf in your client side code:

...
"browser": {
"html-pdf": false
},
...

2. Styling the PDF

To style your PDF, you can either use inline css style or provide the path to your css file. As we are using react components to generate the PDF, it is always good idea to directly use the styles of your components. I haven’t demonstrated this in my example. If you are going to use css file, you can reference your css file either using file://[path-to-css] or http://localhost:[port]/[path-to-css]. It is not suggested to reference your css file using your public domain because you will need extra proxy config on html-pdf (actually for PhantomJS) and it could be slower to fetch the css as well.

3. External Images

If your server is behind a proxy, you probably cannot show external images on the PDF file. In this case, you have to add proxy config using the phantomArgs option in html-pdf. Please also refer to PhantomJS’s documentation.

4. Decorating the PDF file

You can add decorations, such as header, footer, page number, etc in the generated PDF. All these can be configured easily with the html-pdf options. In my example, I have demonstrated how you can add a footer for every page in the PDF file.

<div
id="pageFooter"
style={{
fontSize: '10px',
color: '#666'
}}
>
This is a sample footer
</div>

The footers needs to have an id equals to “pageFooter”. And from my testing, only inline style can be applied to the footer.

That’s it!

With the above approach, PDF can be generated in a Next.js app at server side. And here are the advantages with this approach:

  • React components can be reused to construct the PDF file
  • React components can take props and the PDF content is dynamic
  • You need not to handle PDF generation outside your Next.js app. So much code can be reused in your app
  • Express server isn’t mandatory in my example
  • If you are using Storybook Driven Development, you can test your component well before testing how it looks in the PDF file. It probably saves you much time to fine tune the UI

Finally, please find the working example in my github:

https://github.com/stanleyfok/nextjs-pdf.

Do let me know if you face any challenge and I am always happy to solve with you :)