LoginSign up for free
LoginSign up for free

Make a PDF with React & and avoid the pain of ongoing service management (Part 1/2)

Written by James Lee

Published on Mar 25, 2021

This is the first of a series of guides to get you inspired to start making API first images, videos and PDFs with

Looking for Part 2? Find it here

What we're building

Today we're going to be building a PDF certificate generator. This guide will be split over two parts.

  • Part 1: Getting started, building the React certificate template and importing it into Make.
  • Part 2: Building the certificate generator application and cleaning it up

When you put it altogether this is what we're cooking 👨‍🍳. Check it out

preview of the certificate app


Why are we building a PDF generator?

Not keen on the preamble? Skip to Getting Started

There will come a point in time where a PDF generation service is required functionality for your application. It's a fact of life for anything from an invoice or receipt, to a ticket, or even something professionally printable like a business card or event name badge.

Building a PDF generation service is not a new workload. On the contrary - it's a well documented procedure at this point, especially since the rise in popularity of Headless Chrome services over the last few years.

But with most, the work is still on you as a developer to create a service that scales to meet demand, produces quality output - every time and is flexible enough to meet new capability as it arises.

However these services all fall in the trap of:

  • Ending up being so fit for purpose - as needs and products pivot, the service can't sustain change.
  • They are never as easy as you hoped and what you end up building isn't what you set out to build.
  • What you should be focussed on building and crafting (your application), becomes secondary to the function of it (generating a PDF).

From Andy Fitzsimon's article, Did you ever make, Make?

Maintaining and managing a PDF service, especially an ageing one, is a large investment. But it doesn't have to be, nor does it have to take away from what you want to build.

That's where we come in at Instead of you having to maintain and manage your services and dependencies, let us do it for you. And while we're at it we'll do it in a completely scalable and severless environment so that every PDF will generate as quickly as possible and every PDF will be of the same quality. And by the way we haven't even talked about post processing functionality after the PDF has been generated ... we do that as well.

1. Getting Started

We're going to be creating two react apps with Create React App (CRA). One for our template that we'll import into Make and then the other react app will be our front end application (we'll go through building our app in Part 2).

To get started let's go ahead and create our two react apps.

$ npx create-react-app certificate-template
$ npx create-react-app certificate-app

CRA gives us a lot of lovely functionality out of the box but for these simple apps we just don't need all of that goodness. For sanity's sake we can strip out the following files in both of your newly created apps.

// certificate-app & certificate-template
App.test.js 🗑
index.css 🗑
logo.svg 🗑
reportWebVitals.js 🗑
setupTests.js 🗑

After deleting those files, you'll have to clean up some broken imports in your App.js and index.js

The last thing I would suggest doing is installing a really simple CSS reset into both of your react apps. For me I really like minireset.css

$ cd certificate-template
$ yarn add minireset.css
$ ..
$ cd certificate-app
$ yarn add minireset.css

Once minireset.css has been installed in both apps you can import it the App.js on both applications with the following.

// App.js
import 'minireset.css';
import './App.css';
function App() {
return <div className="App">{/* OUR APP CODE */}</div>;
export default App;

2. Creating our template

Let's spin up our server for certificate-template

$ yarn start

Just as a reminder for this template we're building a certificate template that will need to accept the following:

  • Recipient name (name - string)
  • Completed course name (course - string)
  • Today's date (date - string)

And this is what our lovely certificate will look like.

preview of the certificate template

If you want to cheat you can fork this repo to your Github and skip to Importing the template into

Adding our structure

In our App.js file let's setup the following structure.

// App.js
import 'minireset.css';
import './App.css';
function App() {
return (
<div className="App">
<Icon />
<p className="byline">Certificate of completion</p>
<div className="content">
<p>Awarded to</p>
<h1>Name Surname</h1>
<p>for completing:</p>
<h2>Creating PDFs with React &</h2>
<p className="date">
Issued on <span className="bold">March 15 2021</span>
const Icon = () => (
viewBox="0 0 99 139"
<path d="M0 0H99V138.406L52.1955 118.324L0 138.406V0Z" fill="white" />
d="M25.4912 83.2515C25.4912 79.4116 27.0222 75.7289 29.7474 73.0137C32.4727 70.2985 36.1689 68.7731 40.0229 68.7731C43.877 68.7731 47.5732 70.2985 50.2984 73.0137C53.0236 75.7289 54.5546 79.4116 54.5546 83.2515M40.0229 59.724C40.0229 55.8841 41.5539 52.2014 44.2791 49.4862C47.0044 46.7709 50.7006 45.2455 54.5546 45.2455C58.4087 45.2455 62.1049 46.7709 64.8301 49.4862C67.5553 52.2014 69.0863 55.8841 69.0863 59.724V83.2515"
export default App;

Adding our styles

Let's turn the lights on. Copy the following styles and paste them into the App.css, replacing what was in there.

/* App.css */
@import url(';500&family=Poppins:wght@800&display=swap');
:root {
--blue: #0379ff;
--light-blue: #9ac9ff;
--dark-blue: #0261cc;
--white: #fff;
* {
box-sizing: border-box;
body {
margin: 0;
font-family: 'IBM Plex Sans', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 20px;
.App {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
color: var(--light-blue);
background-color: var(--blue);
background-image: url('data:image/svg+xml;utf8,<svg width="55" height="45" viewBox="0 0 55 45" fill="none" xmlns="" xmlns:xlink=""><path d="M5.49121 44.2515C5.49121 40.4116 7.02223 36.7289 9.74745 34.0137C12.4727 31.2985 16.1689 29.7731 20.0229 29.7731C23.877 29.7731 27.5732 31.2985 30.2984 34.0137C33.0236 36.7289 34.5546 40.4116 34.5546 44.2515M20.0229 20.724C20.0229 16.8841 21.5539 13.2014 24.2791 10.4862C27.0044 7.77095 30.7006 6.24554 34.5546 6.24554C38.4087 6.24554 42.1049 7.77095 44.8301 10.4862C47.5553 13.2014 49.0863 16.8841 49.0863 20.724V44.2515" stroke="%230261CC50" stroke-width="11"/></svg>');
background-size: 160%;
background-position: 90% 150%;
background-repeat: no-repeat;
padding: 2.5rem;
svg {
position: absolute;
top: 0;
.content {
position: absolute;
top: 12rem;
right: 2.5rem;
width: 65%;
.content * {
margin-bottom: 1rem;
.content h1 {
font-family: 'Poppins', sans-serif;
color: var(--white);
font-size: 3rem !important;
line-height: 1;
margin-bottom: 2rem;
.content h2 {
font-size: 2rem !important;
font-weight: 500;
line-height: 1;
.byline {
position: absolute;
right: 2.5rem;
.date {
position: absolute;
bottom: 2.5rem;
font-size: 0.75rem;
.bold {
font-weight: 500;

So with those styles in there your certificate should look something like this.

preview of what the template looks like

If you're OCD like me, you can simulate the export size that we'll be passing to Make by cracking open the Dev Tools (I'm on Chrome so this may differ slightly for other browsers) and clicking on the responsive test tool and popping in 595 x 842, which are the pixel dimensions for an A4 page.

preview of what the template looks like


Adding our functionality

With the Make API you can send custom data to your template before generation. So let's prep our template to accept the custom data we want to send it from our application.

As a refresher, this is what we want our template to handle:

  • Recipient name (name - string)
  • Completed course name (course - string)
  • Today's date (date - string)

When sending custom data to a template Make creates a custom window object called templateProps that your template can then access.

To access this object in our react template we can add the following to our index.js and spread these window.templateProps onto our App.

<App {...window.templateProps} />

Once we do that it's as simple as de-structuring the props that we expect to receive from Make in our App.js and voila we can now accept custom data in our template.

// App.js
function App({ name, course, date }) {
return (
<div className="App">
<Icon />
<p className="byline">Certificate of completion</p>
<div className="content">
<p>Awarded to</p>
<p>for completing:</p>
{date && (
<p className="date">
Issued on <span className="bold">{date}</span>

However on our local environment because we have no concept of the templateProps object, we've got no data!

We can however use defaultProps to simulate what Make would send our template.

// App.js
App.defaultProps = {
name: 'James Lee',
course: 'Creating PDFs with React &',
date: 'March 15 2021',

Prepping & pushing to Github

Now that we've got our template we need to import it into Make.

At the moment Make does not have an application build pipeline. To circumvent this you can build your files locally and push its build folder to Github for import into Make.

To do this successfully we need to do 3 things to our react template before pushing to Github:

  1. Update the build path
  2. Update the .gitignore
  3. Build our template

Updating the build path

By default when you build a react app, CRA assumes that it will be hosted at the the server root. However in our case our template cannot be hosted at the root when imported into Make.

To allow for correct importing you can add the following to your package.json to serve all assets relative to the root.

"homepage": "./",

Updating the .gitignore

By default git will ignore the build folder, but we need to make special allowances so that we can push up the build folder to Github.

To do so, just remove or comment out the /build line in your .gitignore.

# See for more about ignoring files.
# dependencies
# testing
# production
# /build
# misc

Build it!

$ yarn build

Pushing to a Github repo

Once we've done that create a new blank repository on Github and push up your certificate-template repository.

Remember to NOT initialize anything in the repository as we're about to send up everything from our local environment.

setting up repo in Github

$ git add .
$ git commit -m "initial commit"
$ git branch -M main
$ git remote add origin[your-username]/certificate-template.git
$ git push -u origin main

Your template code (including the build directory) should be in your new Github repo.

setting up repo in Github

Importing our template into

Now that we've got out template in Github let's finalize our import.

Jump over to and click Import Template

importing template into Make

Choose the repository we just created

choosing the github repository to import

Don't see your repo?

If you don't see your repo in the list, scroll to the bottom of the page and look out for the Alert (looks like the below) and click the Github button at the bottom of the page to give the correct permissions to access that repo (in my case I needed to do this).

Provide your template with a name and the root directory to your build path which in our case will be /build.

modal defining the settings for the imported template

Import it! 🚀

dashboard for newly imported template

Testing it!

Now that we've imported our template, we can test it via the API playground. You can paste this into the playground and hit Send Test Request.

"size": "A4",
"format": "pdf",
"data": {
"name": "[Your Name]",
"course": "Importing templates into Make",
"date": "Right now"

api playground

Clicking the resultUrl link will open the generated PDF, which should look something like this!

preview of generated PDF certificate

Nice one! You've just generated your first PDF out of Make. 🎉🎉

Concluding Part 1

Give yourself a pat on the back. You've just created your first Make template and generated your very own PDF out the other side.

In Part 2 I'll show you how you can use your new Make template endpoint in your very own certificate generator app. Grab a coffee, snack or whatever you need to do and when you're ready, dive into Part 2.

Go to Part 2