How to create a resume with Gatsbyjs
Online resume
There are a lot of reasons to use a static site generator to generate your resume online:
- it is always nice to have a fast loading time
- you don’t want to take any chance with the site being down when a prospective employer accesses your resume
- it is cheap to deploy: the load on the page should be low, so a free hosting solution like Netlify makes sense
For all those reasons, Gatsby makes a good choice as a platform to publish your resume online.
This article will do exactly that:
- the resume we will build is deployed with Netlify at https://resume-with-gatsby.netlify.com/
- you can find the source code on GitHub at https://github.com/pcarion/resume-with-gatsby
Resume definition format
You will update the content of your resume regularly, so you really don’t want to sprinkle this content inside a static HTML template.
The JSON Resume is an open-source project to create a JSON-based standard for resumes.
Personally, I find that a JSON file is more difficult to update than a yaml file, so we’ll use the format defined by the JSON resume project, but in a YAML format.
The project comes with a sample resume, from the fictional character Richard Hendriks from the excellent series “Silicon Valley” on HBO:
https://github.com/jsonresume/resume-schema/blob/v1.0.0/examples/valid/complete.json
Using a YAML format, the resume looks like this:
---
basics:
name: Richard Hendriks
label: Programmer
image: ''
email: richard.hendriks@mail.com
phone: "(912) 555-4321"
url: http://richardhendricks.example.com
summary: Richard hails from Tulsa. He has earned degrees from the University of
Oklahoma and Stanford. (Go Sooners and Cardinal!) Before starting Pied Piper,
he worked for Hooli as a part time software developer. While his work focuses
on applied information theory, mostly optimizing lossless compression schema of
both the length-limited and adaptive variants, his non-work interests range widely,
everything from quantum computing to chaos theory. He could tell you about it,
but THAT would NOT be a “length-limited” conversation!
location:
address: 2712 Broadway St
postalCode: CA 94115
city: San Francisco
countryCode: US
region: California
Gatsby setup
To install Gatsby, you start with installing the 3 required packages
yarn init -y
yarn add gatsby react react-dom
You can then add the usual scripts in your package.json
file to start the Gatsby server:
"scripts": {
"build": "gatsby build",
"clean": "gatsby clean",
"develop": "gatsby develop"
},
At this stage, you can already start Gatsby with yarn develop
, but there is nothing available for rendering.
Loading the resume in Gatsby
The gatsby-source-filesystem
plugin allows you to load any file in Gatsby.
yarn add gatsby-source-filesystem
To configure this plugin, you create a gatsby-config.js
file where you tell the plugin which directory to parse to read files.
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./resume`,
},
},
],
}
You can then start Gatsby (yarn run develop
) and load the Graphiql server, usually at that URL: http://localhost:8000/___graphql
The gatsby-source-filesystem
will populate the allFile
root with the files it found, as shown by this query:
query MyQuery {
allFile {
edges {
node {
absolutePath
}
}
}
}
the results will show your resume file:
{
"data": {
"allFile": {
"edges": [
{
"node": {
"absolutePath": "<path to>/resume/resume.yaml"
}
}
]
}
}
}
Exploration
The step here is not required, but it shows how you could write your own plugin to parse the resume.
In the gatsby-node.js
file, you could write a very basic plugin to read the content of the resume, using the onCreateNode
method to later create new nodes to expose your resume in the GraphQL API:
async function onCreateNode({ node, loadNodeContent }) {
if (node.internal.mediaType !== `text/yaml`) {
return;
}
const content = await loadNodeContent(node);
console.log(`File: ${node.absolutePath}:`);
console.log(content);
}
exports.onCreateNode = onCreateNode;
When you restart the server, you can see the content of the resume file on screen:
success onPreBootstrap - 0.009s
success createSchemaCustomization - 0.002s
File: <path to>/resume/resume.yaml:
---
basics:
name: Richard Hendriks
label: Programmer
Interesting but there are existing plugins who already do that … and much more.
Transformer plugin: gatsby-transformer-yaml
This plugin is a good candidate for us:
- it reads the yaml file
- parses it
- makes it available if the GraphQL API
You install that plugin with:
yarn add gatsby-transformer-yaml
and the magic happens when you restart the server: 2 new root types appear in the Graphiql interface, allResumeYaml
and resumeYaml
.
If we suppose that the site will contain only one resume, we can get the content of the resume using that query:
query MyQuery {
resumeYaml {
basics {
email
name
phone
}
}
}
and, we get back:
{
"data": {
"resumeYaml": {
"basics": {
"email": "richard.hendriks@mail.com",
"name": "Richard Hendriks",
"phone": "(912) 555-4321"
}
}
}
}
Time to render some data
Let’s create our first page:
- you can delete the
gatsby-node.js
file containing our exploration plugin. - create a
src/pages/index.js
We’ll start with the most basic data extraction (name and email) to make sure that all is in order.
In src/pages/index.js
:
import React from 'react';
const Resume = ({ data }) => {
const resume = data.resumeYaml;
const { basics } = resume;
return (
<React.Fragment>
<h1>{basics.name}</h1>
<h2>{basics.email}</h2>
</React.Fragment>
);
};
export default Resume;
export const query = graphql`
query MyQuery {
resumeYaml {
basics {
email
name
}
}
}
`;
This will give us our first page:
Not quite enough yet to get you a job at Hooli, but it’s a promising start.
Resume rendering
The JSON resume project contains also a library of themes to render resume following the specification.
For the purpose of this presentation, I have chosen the flat theme by Mattias Erming, because
- it is pretty well designed
- it is open source (MIT) - https://github.com/erming/jsonresume-theme-flat
- the styling is easy to extract: one
style.css
file and one template file
Process to build the resume
I won’t describe all the steps to port that template to Gatsby, just the major steps
extra package: gatsby-react-helmet
Helmet is nice addition because it makes it very easy to:
- to set page title
- add external css dependencies
You install that package with:
yarn add gatsby-plugin-react-helmet react-helmet
and add it to your gatsby-config.js
file.
issue with CSS ordering
The way you add a specif css document (the one coming with the flat theme example), is by importing if in the gatsby-browser.js
document:
import "./src/styles/style.css"
The style requires 2 other CSS loaded from a CDN, and you can use Helmet to do that:
<Helmet>
<title>{basics.name}</title>
<meta name="description" content={`resume for ${basics.name}`} />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/css/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/octicons/2.0.2/octicons.min.css"
/>
</Helmet>
The problem is that the local style loaded by gatsby-browser.js
is loaded before the styles loaded by Helmet and that broke the rendering: for example the H1..4 styles were the ones defined by bootstrap, and not the local styles file.
The only solution I found, was to load bootstrap as a dependency and load it from gatsby-browser.js
, before the local style:
import 'bootstrap/dist/css/bootstrap.css'
import "./src/styles/style.css"
porting process
The process involved updating the GraphQL query to import more and more data (the minimal theme only omits the projects
section of the resume).
The final query asks for all the data:
query MyQuery {
resumeYaml {
basics {
email
name
label
phone
url
summary
profiles {
network
url
username
}
}
work {
description
endDate(formatString: "MMM, YYYY")
highlights
location
name
startDate(formatString: "MMM, YYYY")
position
summary
url
}
volunteer {
endDate(formatString: "MMM, YYYY")
highlights
organization
position
startDate(formatString: "MMM, YYYY")
summary
url
}
education {
area
courses
endDate(formatString: "MMM, YYYY")
gpa
institution
startDate(formatString: "MMM, YYYY")
studyType
}
awards {
awarder
date(formatString: "MMM, YYYY")
summary
title
}
publications {
name
publisher
releaseDate(formatString: "MMM, YYYY")
summary
url
}
skills {
keywords
level
name
}
languages {
fluency
language
}
interests {
keywords
name
}
references {
name
reference
}
}
}
and each section of the resume has its own component:
const Resume = ({ resume }) => {
const { basics, work, volunteer, education, awards, publications, skills, languages, interests, references } = resume;
return (
<React.Fragment>
<Helmet>
<title>{basics.name}</title>
<meta name="description" content={`resume for ${basics.name}`} />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/octicons/2.0.2/octicons.min.css"
/>
</Helmet>
<Header basics={basics} />
<div id="content" className="container">
<Contact basics={basics} />
<About basics={basics} />
<Profiles profiles={basics.profiles||[]} />
<Work works={work} />
<Volunteer volunteers={volunteer} />
<Education educations={education} />
<Awards awards={awards} />
<Publications publications={publications} />
<Skills skills={skills} />
<Languages languages={languages} />
<Interests interests={interests} />
<References references={references} />
</div>
</React.Fragment>
);
Pretty easy to read and update.
Deployment
We won’t describe here the actual deployment as there are a lot of articles out there describing how to deploy a Gatsby site.
For instance, the site https://resume-with-gatsby.netlify.com/ was deployed on Netlify with a couple of clicks.