Let’s make a resume in React

William Kwok
9 min readAug 19, 2018

THIS GUIDE IS OUT OF DATE! SEE AN UPDATED METHOD HERE!:

What? You heard correct. Let’s make a Resume using React. I tried searching for a few hours to see if anyone else had done it, but I couldn’t find any clear steps to doing so. I had to hack together a solution that acted exactly how I wanted. These features set my method apart from other existing methods:

  • Export the React object(s) as a Letter or A4 sized PDF with the click of a button, for people interested in collecting a PDF of my resume.
  • Have the ability to highlight text that appears (and even provide linking capabilities)
  • Allow SVGs (from @fortawesome/react-fontawesome) to be embedded easily.

This was surprisingly difficult to figure out. I want to teach people how to do this too.

Why? I created my resume previously in Microsoft Word and PowerPoint, and I was getting frustrated with the difficulty of precision, control, modification, and expansion. As a programmer, I wanted to be able to change a single variable to instantly see my change, and not have to shift five different things up a single pixel, only to just undo my changes because I didn’t like them. I also wanted to put this resume live and inline on my personal website, which is also programmed using React.

Future? I want to expand on this in the future and maybe make an open source tool to allow people to create their resumes and save it online, and even download the code that will let them create it.

I also want to make guides for how to do this with other libraries / frameworks / mostly vanilla JavaScript in the future.

Sorry if in the future this article is obsolete. Please let me know and I’ll update it to reflect changes! As of August 2018, this method works.

When I posted this, someone let me know that there was a library out there called react-pdf that does something very similar to what Kendo below does. They plan on adding native SVG support, but it hasn’t been worked on in a while. I believe that it might be possible to use my trick below to add SVGs to it regardless. I haven’t tried it out, but if you have and have been successful, let me know!

Acknowledgements. I found this great article by Justin Stahlman which explored the difficulties of and different ways to translate HTML to PDF. The article quickly helped me rule out methods to use. I also found a lot of great resources that I actually have used, that I will mention below.

If you want to just see the code or use it as a boilerplate without understanding it, then feel free to skip all the way to the bottom!

How

Let me introduce you to Kendo React PDF by Progress
Progress has created a library that allows for easily doing this. Setting it up is very straightforward if you follow their documents. I also am not entirely sure if it will remain open-use in the future, because it seems their software comes with a cost. Currently, the required packages are on NPM for the public, and don’t require you to pay anything for them, and I will assume that is intended. I would definitely pay for a license if you plan to use this in a for profit product.

There are limitations with this PDF Export library that you can find here. One of which is including svg tags or files. This is a huge limitation in my opinion, that I found a way around. The other limitations are definitely possible to get around in some way too.

The setup is a little all over the place, so here is the run down for getting set up immediately for making a resume template:

1. Install the required dependencies using Node.js v5.0.0 or later

npm install --save @progress/kendo-react-pdf @progress/kendo-drawing

2. Have your React app ready

3. Import the required packages. In my case, I didn’t use savePDF, but you can look up how that is used in their documentation.

import { PDFExport } from '@progress/kendo-react-pdf';

4. Put the PDFExport object into your React object. The props are pretty self explanatory. The ref is used later on for exporting the PDF. Everything inside will be put onto the exported PDF.

<PDFExport paperSize={'Letter'}
fileName="_____.pdf"
title=""
subject=""
keywords=""
ref={(r) => this.resume = r}>
<div>content</div>
</PDFExport>

5. Here is what it looks like so far:

Empty empty empty

6. As you can see, we basically did nothing. Let’s try visualizing the PDF a little better. So how wide or tall should we actually make it? Kendo uses 72 pixels per inch. Because I’m using the Letter size option we want to create an 8.5x11 inch sized div. This means we want to make our div 612 pixels wide, and 792 pixels tall. (8.5 * 72 = 612, 11 * 72 = 792). Let’s also add some styling to make it appear like a piece of paper.

<PDFExport paperSize={'Letter'}
fileName="_____.pdf"
title=""
subject=""
keywords=""
ref={(r) => this.resume = r}>
<div style={{
height: 792,
width: 612,
padding: 'none',
backgroundColor: 'white',
boxShadow: '5px 5px 5px black',
margin: 'auto',
overflowX: 'hidden',
overflowY: 'hidden'}}>
content
</div>
</PDFExport>
Looks like a piece of paper now

Is this the correct size? It is. Progress provides documentation on their sizes here. It might appear small on the page until you download it. I have not put time into figuring out scaling this to be based off width/height percentages instead of pixels, but I’m sure it isn’t too difficult as long as you do some math to figure out correct sizes.

7. Next let’s just add a button that calls the export functionality. I added some styling to move it to above the PDF.

// Add this method to the React 
exportPDF = () => {
this.resume.save();
}
// Add this to the render method
<button onClick={this.exportPDF}>download</button>
Download button!

If you aren’t interested in adding SVGs to your resume, you are basically done! You can start programming the rest of your resume in React, using all the styling and array mapping and stuff that you would normally use in a React application. If you encounter an issue, I encourage you to go out and try and find a way around it, then write about it!

If you do want SVGs, read on.

In my case, I used react-fontawesome, but you can just use raw SVG html. My procedure just required an extra step.

Turning SVGs into Canvas items into images

It is so convoluted to just get this to work correctly. Kendo doesn’t allow SVGs to be input, but they allow for images. I had to research ways to do this effectively and easily. I found a package that allows you to do this in a few steps! Here they are:

1. Install the canvg library. This is one of the pieces that will allow you to do this conversion.

npm install canvg

2. Import canvg (and ReactDOMServer if you’re using libraries like FontAwesome. ReactDOMServer should already be included with React). I also imported react-fontawesome and an icon.

import canvg from 'canvg';
import ReactDOMServer from 'react-dom/server';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

3. Try it out first. Put an SVG into your PDFExport object somewhere. It’ll just not appear when you try to download the PDF. It’ll appear just fine in React, though.

4. How do you fix it?

  • canvg(canvas, html) will take a reference to a canvas object and the html to the SVG (as a string) and put the SVG into the canvas.
  • Then you can use canvas.toDataURL(“image/png”) to return a base64 image object (read more here).
  • If you are using a FontAwesomeIcon object, you must render it and get the html code for it. This is done using ReactDOMServer.renderToStaticMarkup(<FontAwesomeIcon icon={faGithub}/>);
    This function will return html which can be put through the PDFExport as an img tag. Usage: <img src={outputFromCanvasToDataURL} />

There are multiple ways to do this, but I chose the most familiar to me. I created an invisible canvas object and created a reference to it, then called a function that does the conversion.

Note you want to scale your SVG up when you put it into the htmlString string, to make it super large and high quality. Then when putting it into the PDFExport object, make it small. This way, when you export it, the edges are nice and smooth, and not pixellated. The way this method will work is it takes the SVG as is (width and height declared by you) and translates that immediately into a picture. If your SVG is only set to be 20 pixels wide, your image is going to be very small.

To see an extra example, look at my finalized code under this next code block.

// I set a canvas loaded boolean in the constructor
constructor() {
super();
this.canvLoaded = false;
this.githubIcon = null;
}
// function to convert SVG to image. htmlString is optional
convertSVGToImage = (htmlString) => {
// if FontAwesome, run this next part
let htmlString = ReactDOMServer.renderToStaticMarkup(
<FontAwesomeIcon icon={faGithub} />);
// for both FontAwesome and regular SVG:
canvg(this.refs.canvas, htmlString);
this.githubIcon = this.refs.canvas.toDataURL('image/png')
}
componentDidMount() {
this.convertSVGToImage();
}
// Create a conditionally loaded canvas (will only need it once,
// assuming you store your image data somewhere after you run it
// through the function
render() {
return (
<div>
{/* invisible canvas, and note the reference */}
{!this.canvLoaded &&
<canvas ref="canvas" style={{ display: 'none' }}>
</canvas>
}
<PDFExport>...
{this.canvLoaded && <img src={this.githubIcon} />}
</PDFExport>
</div>);
}

This is essentially what I do. Except I store my data in an array that is iterated through, and I pass my canvas to the function. You can see this in the code below. Sorry for poor formatting. See the code with better formatting on github.

class App extends Component {
resume;
constructor() {
super();
this.iconsToConvert = [
{
icon: faGithub,
alt: 'github icon'
},
{
icon: faMedium,
alt: 'medium icon'
}
]
this.canvLoaded = false;
}
exportPDF = () => {
this.resume.save();
}
convertSvgToImage = (arr) => {
let canv = this.refs.canvas;
if (!this.canvLoaded) {
this.canvLoaded = true;
canv.getContext("2d");
arr.forEach((d, i) => {
let htmlString = ReactDOMServer.renderToStaticMarkup(
<FontAwesomeIcon icon={d.icon} size={"3x"} style={{ color: '#005696', height: '500px', width: '500px' }} />
);
canvg(canv, htmlString);
d.icon = canv.toDataURL("image/png");
});
this.setState({});
}
}
componentDidMount() {
this.convertSvgToImage(this.iconsToConvert);
}
render() {
return (
<div style={{ height: '100vh', width: '100vw', paddingTop: 20, backgroundColor: 'gray' }}>
{!this.canvLoaded && <canvas ref="canvas" style={{ display: 'none' }}>
</canvas>}
<div style={{ textAlign: 'center', marginBottom: 10 }}><button onClick={this.exportPDF} style={{ margin: 'auto' }}>download</button></div>
<PDFExport paperSize={'Letter'}
fileName="_____.pdf"
title=""
subject=""
keywords=""
ref={(r) => this.resume = r}>
<div style={{
height: 792,
width: 612,
padding: 'none',
backgroundColor: 'white',
boxShadow: '5px 5px 5px black',
margin: 'auto',
overflowX: 'hidden',
overflowY: 'hidden'
}}>Hi!
{this.canvLoaded && this.iconsToConvert.map((iconObject, index) => {
return <img src={iconObject.icon} key={'img-' + index} alt={iconObject.alt} style={{ height: 25, width: 25 }} />
})}
</div>
</PDFExport>
</div>
);
}
}
What the output looks like

Final words / tl;dr

You can find the code file for this here (in src/App.js)

You can check out a full example on my website here (click on resume) with the code for it here (in src/components/Resume.js — careful though, my styling is very messy and I also perform the process outlined in this article in a very unoptimized way).

Questions or comments or suggestions? Leave them here! I’m constantly trying to learn more about the JavaScript ecosystem, and I might have a totally unconventional way of doing things. I’d love to hear what I could do to improve, or if my solution to this was woefully out of date or not. If you all find a better solution, I’d love to hear that too!

Links

📝 Read this story later in Journal.

🗞 Wake up every Sunday morning to the week’s most noteworthy Tech stories, opinions, and news waiting in your inbox: Get the noteworthy newsletter >

--

--