Deploy a Static React Blog using GatsbyJS and Github

We'll be creating a blog using GatsbyJS, a static site generator for React. GatsbyJS takes your content, either static files or external data from an API, and generates pages that are hardcoded with the data. Rather than calling the API for your latest blog posts for each request, all your posts are pre-cached into separate HTML files. This allows you to serve your entire website over a CDN. And without any external API calls or even server-side rendering, the website loads lightning fast -- and is offline-friendly.

Today we'll be developing a static blog generated by GatsbyJS, written in Markdown, and we'll deploy on Github Pages to host the blog.

But first, what is GatsbyJS?

GatsbyJS is a generator that allows you to code React apps that get compiled into static assets (HTML + JS). Each page is a technically React component that gets converted into an HTML and JS file when it's time to build the production site. If you've ever worked with a generator like Jekyll, which converts code like Liquid and Markdown into HTML, you'll be familiar with this kind of concept.

What makes GatsbyJS special is it's implementation of GraphQL. All of your content is served through a GraphQL server on the development side. When it comes time to compile the static assets, GatsbyJS queries the GraphQL server for the data and inserts it into your HTML files.

And what the heck is a JAMstack?

Static websites are growing in popularity with the JAMstack revolution. JAM stands for Javascript, API, and Markup. What it basically means is your site is only comprised of:

  • Javascript (usually a framework like React)
  • API (like an RSS feed, or JSON API endpoint) optional
  • Markup (HTML, CSS, any media like images)

The goal is to create a website is comprised of only client-side HTML + CSS + JS. No need to install Node, Ruby, PHP, or any other server-side language. This means we could even deploy this directly on a CDN like S3 on AWS or Netlify.

When a website is made this simple, you can deploy it nearly anywhere, since most servers support HTML, CSS, and JS.

There are plenty of benefits to making your website static, from lighting fast load times to decreased server load, and Gatsby makes it fairly easy to pump out your own. You can find a great 'Getting Started' guide on the official GatsbyJS site, as well as many of the concepts we convey in this tutorial. If you get lost, I'd poke around there and see if it helps paint a clearer picture.

Let's build and deploy a static blog!

Installing Gatsby

Using the CLI

You can either install Gatsby using their CLI, which is recommended:

npm install --global gatsby-cli

Then run this command in the folder where you want the project:

gatsby new gatsby-blog

Classic Git Way

Or you can clone the repo from Github and run an NPM install:

git clone https://github.com/gatsbyjs/gatsby.git gatsby-blog && cd gatsby-blog && npm install

Note if you opt against installing the CLI, you'll have to run NPM scripts instead of gatsby commands when building for development or production.

Spin up the server

Run the following command to start up your GatsbyJS blog locally:

gatsby develop

This command runs the build process, compiling the code into static assets, and gives you access to your GatsbyJS site at http://localhost:8000/. And to make development easier, when you update your code while this is running, it'll re-compile -- allowing you to refresh and see changes instantly.

Creating the content

Our blog will use Markdown files to contain and display our posts. We'll be using the standard Markdown format with a top header. Make a new file in src/blog/first-blog-post.md:

---
title: My first blog post
date: "2018-04-20"
---

Do you enjoy Gabe the Dog? He is the immortal lead singer of Bork, a European band that does covers of popular pop songs from the 80s, 90s, and today.

<iframe width="560" height="315" src="https://www.youtube.com/embed/c--etqIJcow?ecver=1" frameborder="0" allowfullscreen></iframe>

Now that we have some content, let's display it on the website.

Grabbing our Markdown files

GatsbyJS uses components to create pages, so we could literally just create new JS files for each blog post. But that's messy and inefficient. So what do we do instead? Gatsby offers the ability to create source plugins that pull data from certain endpoints, like RSS, Medium, or Github. We're going to make Markdown in the same folder as the Gatsby project, so we'll be using the Filesystem source plugin to grab files locally.

We'll also install a transformer plugin, which takes GraphQL data and processes it. In our particular case, we want to take our data and process the Markdown into HTML. Run the following command to install that plugin:

npm install --save gatsby-source-filesystem gatsby-transformer-remark

And add the following JSON to your config to enable both plugins. If you look close at the path property of the filesystem plugin, we load our blog articles from the blog folder:

plugins: [
    // react-helmet is included by default with gatsby
    `gatsby-plugin-react-helmet`,
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `src`,
        path: `${__dirname}/src/blog/`,
      },
    },
  ],

Restart your development server to commit the changes (hit CTRL + C to terminate the server and then run gatsby develop again). Now run this GraphQL query on your local GraphiQL development panel:

{
 allFile {
  edges {
    node {
      name
      extension
    }
  }
 }
}

Enter the query and hit CMD + Enter. You should see a JSON object on the right side with the blog post we just made.

Making pages dynamically

We could easily start querying this data on our pages and displaying our posts. If you paste this into src/pages/index.js you'll see all your files printed out in your console:


import React from "react"

export default ({ data }) => {
  // displays an object of the query data in console
  // simply access what you need using a map function
  // data.allFile.edges.map()
  console.log(data)
  return <div>Hello world</div>
}

export const query = graphql`
  query MyFilesQuery {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }
`

Which makes for a great frontpage with a list of all of our posts, but we end up at the same dilemma as before. If we want separate pages for each blog post, we have to make new components to query each page individually. That's where the GatsbyJS API comes into play.

GatsbyJS is capable of taking a GraphQL query and creating pages for every object based off a template. For every Markdown file we create, when we build our GatsbyJS website, it'll get run through a template to create a page. We end up with HTML files for each page with the Markdown parsed into readable text.

Paste this into your gatsby-node.js file.

const path = require(`path`);
const { createFilePath } = require(`gatsby-source-filesystem`);

exports.onCreateNode = ({ node, getNode, boundActionCreators }) => {
    const { createNodeField } = boundActionCreators
    if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode, basePath: `pages` })
        createNodeField({
            node,
            name: `slug`,
            value: slug,
        })
    }
};

exports.createPages = ({ graphql, boundActionCreators }) => {
    const { createPage } = boundActionCreators
    return new Promise((resolve, reject) => {
        graphql(`
      {
        allMarkdownRemark {
          edges {
            node {
              fields {
                slug
              }
            }
          }
        }
      }
    `).then(result => {
                result.data.allMarkdownRemark.edges.forEach(({ node }) => {
                    createPage({
                        path: node.fields.slug,
                        component: path.resolve(`./src/templates/blog-post.js`),
                        context: {
                            // Data passed to context is available in page queries as GraphQL variables.
                            slug: node.fields.slug,
                        },
                    })
                })
                resolve()
            })
    })
};

First, we'll create slugs based off our Markdown file names, and add them to the GraphQL query results. Then we'll use the createPages API to make new pages based off a GraphQL query for the Markdown posts. Then we'll use the createPage function to actually generate the page based off the new file path and component that'll act as the template.

When Gatsby runs the build process, it'll run this script as well, which will trigger the creation of pages.

There's not much to explain here since this is just very API specific code. It's simple enough to be self-explanatory, and anything that's unclear is probably opinionated decisions from the API.

The Blog Template

Now that our blog posts are ready to get converted into static pages, let's actually create the template we referenced above ./src/templates/blog-post.js. Make a new file there and paste this into it:

import React from "react";

export default ({ data }) => {
    const post = data.markdownRemark;
    return (
        <div>
            <h1>{post.frontmatter.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.html }} />
        </div>
    );
};

export const query = graphql`
  query BlogPostQuery($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`;

Show me the posts!

We've got our blog posts as Markdown ready to get converted, we have the React template, the only thing left is linking to the posts.

Head over to your index.js file and paste the following:

import React from "react";
import Link from "gatsby-link";

export default ({ data }) => {
  console.log(data);
  return (
    <div>
      <h1 style={{ display: 'inline-block', borderBottom: '1px solid' }}>
        Amazing Pandas Eating Things
      </h1>
      <h4>{data.allMarkdownRemark.totalCount} Posts</h4>
      {data.allMarkdownRemark.edges.map(({ node }) => (
        <div key={node.id}>
          <Link
            to={node.fields.slug}
            css={{ textDecoration: `none`, color: `inherit` }}
          >
            <h3 style={{ marginBottom: '4px' }}>
              {node.frontmatter.title}{" "}
              <span style={{ color: "#BBB" }}>— {node.frontmatter.date}</span>
            </h3>
          </Link>
            <p>{node.excerpt}</p>
        </div>
          ))}
    </div>
      );
      };

      export const query = graphql`
  query IndexQuery {
        allMarkdownRemark(sort: {fields: [frontmatter___date], order: DESC}) {
        totalCount
      edges {
        node {
      id
          frontmatter {
        title
            date(formatString: "DD MMMM, YYYY")
    }
          fields {
        slug
      }
      excerpt
    }
  }
}
}
`;

We query using the MarkdownRemark endpoint and grab the titles, slugs, and excerpts of our latest blog posts. Then we loop through the data to show the data, while using the <Link> component to link directly to the blog post (using the slug).

Dont forget, you can always test out the query in the GraphiQL dev panel to make sure you have the right properties.

If you restart your dev server at this point, you should see a list of the Markdown files you created. And if you click them, they'll take you to another page with the complete blog post.

Congratulations! You've built your first static blog. You can stop here and just run gatsby build to make a production-ready version of your blog available in the public folder. Upload that directly to your FTP or web host and you're good to go.

But why stop there? One of the principles of the JAMstack is using Git for version control. This allows you, or any dev on your team, to easily clone the website's repository and create an exact replica of the entire website. It also allows you to quickly push new changes to the server, rather than uploading files individually through an FTP.

Let's git started

If you haven't already installed Git on your computer, head over to the official website and download it. Then open up Terminal, cd to your project's root, and run the following command:

git init

This creates a new Git repository in your folder. Now let's commit all the changes we've made to the new repository:

git add -A && git commit -m "Your Message"

This takes all the files in the folder and adds them to the Git repo. When you make changes, you'll be able to track the differences between previous versions before each commit (git diff). The message you leave usually hints at what kind of changes were made to the code. In this case, something like "Initial commit" or "1.0" is appropriate.

Connect with Github

Connecting with Github allows you to promote the highest accessibility for developers looking to access the website's source code, and to take advantage of Github's free hosting](https://pages.github.com/). You'll sign up for a Github account if you don't already have one, create a public repo, and push (or upload) the project files to Github through Git commands.

Sign up on Github

  1. Create a new account on Github
  2. Login to your account.
  3. Click the plus sign in the top menu and click "New repository" from the dropdown.
  4. Name your repo anything you'd like, then click the big green "Create repository" button.

Sync up your repo with Github

To make syncing up to Github a single click we'll install gh-pages. This is a Github Pages package that pushes changes to Github and updates the page. Run the following command to install the package:

npm install gh-pages --save-dev

You'll also need to modify the package.json with a new script. This script runs the gatsby build process, then runs the gh-pages command to deploy to Github. Add the following line into the scripts section:

  {
        scripts: {
            // ...you'll see build, develop, format, etc above this....
            "deploy": "gatsby build --prefix-paths && gh-pages -d public",
        }
    }

And since Github Pages hosts the blog in a subdirectory (e.g. yourname.github.io/this-subdirectory/), we have to add a path prefix to the configuration gatsby-config.js to let GatsbyJS know it's not in the root:

{
  siteMetadata: {
    title: `Your site Name`,
  },
  pathPrefix: "/your-repo-name",
}

Deploy!

Go to your new repo on Github, click the Clone button, and copy the URL (ending in .git). Then run the following command to add a "remote" repo to your local git repo:

git remote add origin http://github.com/username/repo-name.git

Now we can build the site and push it to Github. Type in the following command, enter in your Github password when prompted, and profit!:

npm run deploy

The public folder of your blog will be uploaded to the gh-pages branch of your repo. If you click the dropdown labeled Branch: master you should see the gh-pages branch.

Browse your blog

Head back over to your repository on Github and see if you successfully pushed (or uploaded) your files. If it worked, head over to the project settings page. Here, you'll want to make sure Github Pages is enabled and that it's set to the gh-pages branch.

You should be able to access the blog by going to http://yourusername.github.io/repo-name/.

Maybe not the Wordpress 5-minute install

It might not be the most lightning fast blog creation out there, between the time it takes to install npm packages and the time you waste fussing with git. Though you have to admit that in a fairly short time span we were able to create a static blog ecosystem that deploys instantly. It's incredible to see the potential of GatsbyJS, and the different kind of experiences you can create compared to standard CMS platforms like Wordpress or Drupal.

If you've ever been thinking about taking the leap into a progressive web application (PWA), you want to try static, or you've just been interested in migrating off Wordpress -- I hope this guide helped you discover an alternative to the mainstream blogging experience.

The potential is endless

This is the first part in a series of articles we'll be writing featuring GatsbyJS. We've just dipped our toe in the water here, there's a wealth of plugins and potential we've yet to explore with this framework. We'll be looking into creating projects that explore or push the limitations of the JAMstack, from a portfolio site using the Behance API, to a static e-commerce store using Stripe, to building a true JAM app on a CDN with automatic and atomic builds.

Keep an eye on the #GatsbyJS tag to see our next post!

Find the example site here, and the final example repo here.

Stay regular,
Oscar


Keep Reading:

Oscar

Oscar is an artist and engineer who's been creating cannabis brands and media experiences for over 10 years, and developing full-stack applications for over 15 years.