Make a PDF with React & Make.cm and avoid the pain of ongoing service management [Part 1/2]
Tags: react, pdf, javascript, tutorial
Published: 2021-03-25 07:18:47

{% collapsible Looking for Part 2? %}
{% link https://dev.to/jamesrplee/make-a-pdf-with-react-make-cm-and-avoid-the-pain-of-ongoing-service-management-part-2-2-dm6 %}
{% endcollapsible %}

## 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](https://dev.to/jamesrplee/make-a-pdf-with-react-make-cm-and-avoid-the-pain-of-ongoing-service-management-part-2-2-dm6)**: Building the certificate generator application and cleaning it up

<p style="text-align: center; font-weight: bold">
  When you put it altogether this is what we're cooking 👨‍🍳. 
  <a href="https://certificate.make.cm/">Check it out</a>
</p>

![preview of the certificate app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02ju25lnq4rmckx1rn8d.png)

<br />

##### Resources

{% github makecm/certificate-app no-readme %}

{% github makecm/certificate-template no-readme %}

---


## Why are we building a PDF generator?

{% collapsible Read more %}

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](https://make.cm/blog/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 Make.cm. 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.

{% endcollapsible %}

---

## 1. Getting Started

We're going to be creating two react apps with [Create React App (CRA)](https://create-react-app.dev/docs/getting-started/). 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](/blog/make-a-pdf-2)).

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

```shell
$ 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.

```jsx
// certificate-app & certificate-template

/node_modules
/public
/src
  App.css
  App.js
  App.test.js 🗑
  index.css 🗑
  index.js
  logo.svg 🗑
  reportWebVitals.js 🗑
  setupTests.js 🗑
.gitignore
package.json
README.md
yarn.lock
```

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`

```shell
$ 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.

```jsx
// 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`

```shell
$ 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ynvnrj2x14kn7hhrze8y.png)

If you want to cheat you can fork this [repo](https://github.com/makecm/certificate-template) to your Github and skip to Importing the template into Make.cm.

<br />

### Adding our structure

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

```jsx
// 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 & Make.cm</h2>
      </div>

      <p className="date">
        Issued on <span className="bold">March 15 2021</span>
      </p>
    </div>
  );
}

const Icon = () => (
  <svg
    width="99"
    height="139"
    viewBox="0 0 99 139"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
  >
    <path d="M0 0H99V138.406L52.1955 118.324L0 138.406V0Z" fill="white" />
    <path
      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"
      stroke="#0379FF"
      strokeWidth="10.6193"
    />
  </svg>
);

export default App;
```

<br/>

### 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.

```css
/* App.css */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;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="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5rhls2h1ifxa6ir8ws1.png)

<br />

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.

<br />

![preview of what the template looks like](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/py9k2vpp26mqzabefdhl.png)

**😍 BEAUTIFUL 😍**

<br />

### 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.

```jsx
//index.js

ReactDOM.render(
  <React.StrictMode>
+    <App {...window.templateProps} />
  </React.StrictMode>,
  document.getElementById('root')
);
```

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.

```jsx
// 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>
        <h1>{name}</h1>
        <p>for completing:</p>
        <h2>{course}</h2>
      </div>

      {date && (
        <p className="date">
          Issued on <span className="bold">{date}</span>
        </p>
      )}
    </div>
  );
}
```

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.

```jsx
// App.js

App.defaultProps = {
  name: 'James Lee',
  course: 'Creating PDFs with React & Make.cm',
  date: 'March 15 2021',
};
```

## 3. 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.

```jsx
//package.json

"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`.

```git
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
# /build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
```

### Build it!

```shell
$ 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2at8wch2pt7tqqn02gg.png)

```shell
$ git add .
$ git commit -m "initial commit"
$ git branch -M main
$ git remote add origin git@github.com:[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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1u0leiyimajglgrof4f.png)

## 4. Importing our template into Make.cm

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

Jump over to [app.make.cm](http://app.make.cm) and click `Import Template`

![importing template into Make](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0wfswxl7pi32obvwvex.png)

Choose the repository we just created

![choosing the github repository to import](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fh1u3cy4bwtnzoi8q7e.png)

##### Don't see your repo?

{% collapsible Read more %}

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 Make.cm the correct permissions to access that repo (in my case I needed to do this).

![UI alert telling people to connect their repo to Make](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ur2y3y9xoi63z0ifszc.png)

{% endcollapsible %}

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4mzx2ovgy4glrix8m5ld.png)


Import it! 🚀

![dashboard for newly imported template](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7hhsskiq956wolu9wqk.png)

<br />

### 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**.

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

![api playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6clrd37984fuaem3adt.png)

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

![preview of generated PDF certificate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hsrz67xs6z55hkqu2fx8.png)


**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.

{% link https://dev.to/jamesrplee/make-a-pdf-with-react-make-cm-and-avoid-the-pain-of-ongoing-service-management-part-2-2-dm6 %}