How to build a blogging site with Gatsby

Build a blogging site with Gatsby

Frameworks like React only send JavaScript down to clients, which is then used to create the elements onscreen. The HTML that loads on the page is minimal, as all of the content is generated on the client side after everything has loaded.

In projects that have dynamic data, such as a blog or a shop, the JavaScript has to come down first before any other data can be fetched. Even when using speed-boosting techniques such as code splitting, if the bundle fails to download, the entire site will stop working.

Static-site generators take dynamic content and create pre-built pages ready to serve. If the data does change, the project can be rebuilt and the new content served. The end result is improved speed, scalability and – without constant connections to a database – security. While the approach is not ideal for constantly changing data such as a rolling news site, many projects can benefit.

Gatsby is a static-site generator that is built upon React. Through the use of GraphQL and plugins, it can take data from different sources and pass them into components. Once this is done, it analyses the project and generates HTML files to serve to clients, while React and the application logic gets downloaded in the background.

Download the files for this tutorial.

Get started

Build a blogging site with Gatsby: Get started

After it has been initialised, Gatsby provides an example site to get started, including two basic page components

To start off, we can have Gatsby scaffold out a basic project for us. It provides a development server we can use along with a few useful developer tools. Make sure Node and npm are up-to-date and run npx gatsby new gatsby-site on the command line, with "gatsby-site" being the folder to build into.

Within that folder, Gatsby provides a few commands as part of the initialisation process. Running these will make building the site much easier. Run npm run develop to start the development server and see any changes update automatically in the browser.

All content lives within the /src folder and any setup files are prefixed with "gatsby-" in the project's root. Everything works through components, which do not need to have any additional structure or behaviour in order to be pre-built with Gatsby.

The contents of the /src/pages folder is special. Gatsby will pick up any component within that folder to create a page. Open up index.js and clear out the contents of the component. Notice that the page updates in the browser as we save.

const IndexPage = () => (
  <Layout>
    {/* Empty */}
  </Layout>
)

Build a page

Each blog post needs its own page. It's possible to make a page component for each new post but this creates a barrier for those not used to React and will also require more maintenance in the future as designs change. 

Gatsby has a library full of plugins ready to change the way it generates pages, which can be found at gatsbyjs.org/plugins. There we can find two types of plugin – 'source' and 'transformer'.

A source plugin will take data from a source and convert them into 'nodes', which is how Gatsby deals with the information within a site. The data can be fetched locally for files like images or remotely for external data such as a database.

A transformer plugin can then take these nodes and create new ones to make things easier for Gatsby to work with. For example, YAML files can't be parsed by default but a transformer plugin can turn the nested syntax within them into objects to read inside components.

Markdown is a common format used for text because it's versatile, easy to read and can be converted to HTML. The source plugin "gatsby-source-filesystem" can take files locally and convert them to nodes, while the transformation plugin "gatsby-transformer-remark" uses Remark to convert Markdown into something we can pick up and query with GraphQL.

The starter project already comes with the source plugin. Install the other by running npm install gatsby-transformer-remark. Please note that the development server may need to be restarted for it to be picked up.

Set up plugins

With the plugin installed, Gatsby needs to be told how to use it. All this logic is held within the "gatsby-config.js" file generated at the start. It comes with a few plugins already set up out of the box but we need to add ours to the mix to be able to pick up and use Markdown.

Simple plugins that have no set-up procedure can be added in as strings. As the transformation plugin has only one job, it does not need setting up. However, the source plugin has to be told where to find the posts. Add them to the bottom of the plugins array.

plugins: [
  […]
  "gatsby-transformer-remark",
  {
    resolve: `gatsby-source-filesystem`,
    options: {
      name: `pages`,
      path: `${__dirname}/src/pages`
    }
  }
]

Because each post will become its own page, it makes sense to add them to the src/pages folder. These setting options are telling Gatsby to look into that folder and pull out any files. 

Create a blog post

Build a blogging site with Gatsby: Create a blog post

Helmet can be used to add <meta> elements on a per-post basis, such as using post tags as keywords

With the plugins in place, we can create our first post. Create a folder called "my-first-post" and add a "my-first-post.md" Markdown file within it. This convention enables us to add any related files – such as images – alongside the post itself.

We need to add some Markdown to this post so we know that it's working as expected.

---
path: /post/my-first-post
date: 2018-12-01
summary: Post summary
tags: [my, first, post]
title: My First Post
---
This is my first post!

The content between the dashes at the top of the file is called 'front matter'. This will contain metadata around the post that is being written, such as the date and title. All of this data will be picked up by Remark and can be queried for within GraphQL.

The crucial piece of front matter in this case is the path value. This is where the post will live and will need to be unique. Gatsby will read the path and make a new page there.

Before we can show the posts, we need a page component to display the post. It will need to be able to take the values as props and display the content as a block of HTML.

Create a new component at "src/BlogPost.js". The information about each post will come through as a data prop from GraphQL.

import React from 'react'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import Layout from '../components/layout'
export const Blog = ({ data: { markdownRemark } }) => {
  const { frontmatter, html } = markdownRemark
  return (
    <Layout>
      <Helmet title={frontmatter.title} />
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </Layout>
  )
}

The "react-helmet" package bundled with Gatsby enables us to update values that would usually live within the <head> of a HTML page. Here we are setting the title of the post to be the <title> of the page itself. There are plenty of other options it accepts, which you can find out more about at github.com/nfl/react-helmet.

Query for data

Build a blogging site with Gatsby: Query for data

Gatsby comes with GraphQL, which can be used to help test out queries. Run the development server and head to localhost:8000/___graphql

At this point, Gatsby has no data powering this page. We need to fetch data from the Markdown files to populate this information. To do this, we can use GraphQL – a querying language created by Facebook that pulls in relevant data into React components. In short, GraphQL defines the structure the data will be returned through the use of nested objects. That way, we only query for data we will actually be using.

Gatsby provides a template literal function that can interpret the queries. It will detect any use of it and pass its results as props into the component. This means we can add the query within the same file and keep related logic together.

export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "D MMMM YYYY")
        title
      }
    }
  }`

Inside this query, we are asking Gatsby for all the Markdown nodes with a path that matches the page we are on. If it finds one, it will then pass the rendered HTML, date and title of the post on to the component.

Writing queries this way is limited only to page components. Any other components that need query for nodes must use <StaticQuery> and load it up front. At this point, the development server may warn about this for the BlogPost component, but this is because it is not aware it will become a page component yet. Let's change that.

Generate pages

Build a blogging site with Gatsby: Generate pages

If you see a "getNodesByType is not a function" error, an outdated version of Gatsby has been downloaded. Running npm update fixes this

By default, Gatsby only makes pages for components within /src/pages, meaning that we need to create pages some other way.

Gatsby exposes a few methods from its build process to help access the data nodes. These can be accessed through "gatsby-node.js" in the root of the project. In this case, we will use GraphQL to fetch all the blog posts and let the createPages callback generate a page for each one. As this is an asynchronous action, we need to return a Promise so Gatsby can carry on with the build process.

const path = require('path')
exports.createPages = ({ actions, graphql }) => {
  return graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              path
            }
          }
        }
      }
    }
  `)
}

The first part of the callback is a query that fetches the path for each post, as each individual page will then get its own data. The GraphQL call returns a Promise that will contain all the posts. We can use the data from that to generate some pages.

.then(result => {
  if (result.errors) {
    return Promise.reject(result.errors)
  }
  const blogPostTemplate = path.resolve('src/components/BlogPost.js')
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    actions.createPage({
      path: node.frontmatter.path,
      component: blogPostTemplate,
    })
  })
})

If the query encounters an error, halt the build process to figure out why. If everything is fine, fetch the component made and call the createPage method to generate a page at the provided path. 

With the pages generating, all that's needed now is a way to find them. We can use a query on the existing index page component to do that.

export const pageQuery = graphql`
  query {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          frontmatter {
            path
            title
          }
        }
      }
    }
  }
`;

In this query, we are only interested in the titles and path to the post. We also pass some parameters to Remark to get the most recent posts in reverse order. This query in particular is checking the date from the front matter on each post. The syntax for each query will depend on the plugin used to generate it.

Build a blogging site with Gatsby: Styling content

With the content in place, the blog can be styled using any CSS technique, including CSS-in-JS solutions such as styled-components

Lastly, the component needs updating to make use of the data. The "Link" component supplied by Gatsby enables it to know which components it needs to render that link and will make sure it fetches the right bundles accordingly.

const IndexPage = ({ data }) => {
  return (
    <Layout>
      {data.allMarkdownRemark.edges.map(
        ({ node: { frontmatter: { path, title }}}) => (
          <Link key={path} to={path}>
            {title}
          </Link>
        ))}
    </Layout>
  )
}

With that, our blog is done. All that remains is to smarten it up and have Gatsby build a production-ready website. By running npm run build it can strip out any enhancements used for development and generate bundles ready to deploy. Once finished, the "public" folder can then be uploaded anywhere that can serve static sites. 

This article was originally published in issue 314 of net, the world's best-selling magazine for web designers and developers. Buy issue 314 here or subscribe here.

Related articles: