How to create a resume with Gatsbyjs
Tags: gatsby, resume, netlify
Published: 2020-01-26 19:36:45


*Originally published at https://pcarion.com/article/gatsby-resume*

# 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](https://www.netlify.com) makes sense

For all those reasons, [Gatsby](https://www.gatsbyjs.org/) 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/](https://resume-with-gatsby.netlify.com/)
- you can find the source code on GitHub at [https://github.com/pcarion/resume-with-gatsby](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](https://jsonresume.org/) 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://www.hbo.com/silicon-valley): 

[https://github.com/jsonresume/resume-schema/blob/v1.0.0/examples/valid/complete.json](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`](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:

![First view](https://pcarion.com/static/Untitled-7d499c186d7eba460e5b21920d9c4170.png)

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](https://jsonresume.org/themes/) to render resume following the specification.

For the purpose of this presentation, I have chosen the [flat theme](https://themes.jsonresume.org/theme/flat) by [Mattias Erming](https://github.com/erming), because

- it is pretty well designed
- it is open source (MIT) - [https://github.com/erming/jsonresume-theme-flat](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>
      <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>
        <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/](https://resume-with-gatsby.netlify.com/) was deployed on [Netlify](https://www.netlify.com) with a couple of clicks.