Martin Brochhaus

How I Built This Blog with GatsbyJS and Netlify

December 24, 2018 ยป 11 min read

GatsbyJS and netlify are getting quite a lot of buzz recently. If you love to work with React, and if you would like to maintain your personal website as a static website, then this stack probably is for you.

At my company Bitlab Studio we constantly push ourselves to find the best way to build and host secure, fast and reliable web applications for our clients. When I saw how GatsbyJS and netlify took off, almost taking the web-design community by storm, I wanted to see what the hype is all about.

I decided to just give it five minutes and see how far I can get. Approximately 8:35hrs later I had this blog online. I found it to be a really fun exercise, so by putting out this guide, I hope to encourage you to try the same. I would estimate that by just following this guide, you should have your blog online in one or two hours. Of course you will then spend another four to eight hours coming up with beautiful styles for your blog.

We will go through the following steps:

When I walked down this rabbit hole, I started with this official step by step guide by netlify.

Create your GatsbyJS repository

First we should create a new repository on Github. After that, we will initialize the repository on our own machine. I like to save all my projects in a folder called ~/Projects, obviously this might be different on your machine.

cd ~/Projects
mkdir YOURBLOGNAME
cd YOURBLOGNAME
npm install -g gatsby-cli
gatsby new YOURBLOGNAME
cd YOURBLOGNAME
npm install gatsby-cli --save
git init
git add .
git commit -am "Initial commit"
git remote add origin git@github.com:USERNAME/YOURBLOGNAME.git
git push -u origin master
gatsby develop

At this point, we should be able to see our new Gatsby site in our browser

For the rest of this guide, we will be installing many different gatsby-plugins. The process is always similar:

  1. Install the plugin via npm
  2. Add the plugin settings to gatsby-config.js
  3. Maybe add or update some other files

Add Google Analytics

For starters, let's install a plugin that loads Google Analytics. This plugin also comes with a nice <OutboundLink> component that sends a custom "Outbound Link" event to Google Analytics when people click those links.

Official docs: gatsby-plugin-google-analytics

cd ~/Projects/YOURBLOGNAME/YOURBLOGNAME
npm install --save gatsby-plugin-google-analytics

After installing the plugin, we need to add it's settings to gatsby-config.js:

// gatsby-config.js
module.exports = {
  // more settings here...
  plugins: [
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: 'UA-XXXXXXXX-X',
        head: false,
        anonymize: true,
        respectDNT: true,
      },
    },
    // more plugins here...
  ],
}

Add Typography.js

I actually never used this before, but it seems that the Gatsby community likes to use this library and I must say, it is indeed pretty cool.

Official docs: gatsby-plugin-typography

cd ~/Projects/YOURBLOGNAME/YOURBLOGNAME
npm install --save gatsby-plugin-typography react-typography typography

Once again, after installing the plugin, we need to add it's settings:

// gatsby-settings.js
module.exports = {
  // more settings here...
  plugins: [
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography.js`,
    },
    // more plugins here...
  ],
}

We also need to create the file src/utils/typography.js. Here we can chose our base-font-size and base-line-height and also define a bunch of Google fonts that we would like to import in our site:

// src/utils/typography.js
import Typography from 'typography'

const typography = new Typography({
  baseFontSize: '20px',
  baseLineHeight: 1.8,
  headerFontFamily: ['Lato', 'sans-serif'],
  bodyFontFamily: ['Lora', 'serif'],
  googleFonts: [
    {
      name: 'Lato',
      styles: ['400', '400i', '700', '700i'],
    },
    {
      name: 'Lora',
      styles: ['400', '400i', '700', '700i'],
    },
  ],
})

export default typography
export const rhythm = typography.rhythm
export const scale = typography.scale

You can learn more about Typography.js here. As far as I understand it, all font sizes should be return values from the scale() function, so that everything is a multiple or fraction of the base font size. Likewise, all paddings and margins should be return values from the rhythm() function, which would be multiples or fractions of the base line height.

Add emotion

I'm a big fan of "CSS in JS" and it seems that emotion is currently the best library for this, so let's add it to our project:

Official docs: gatsby-plugin-emotion

cd ~/Projects/YOURBLOGNAME/YOURBLOGNAME
npm install --save gatsby-plugin-emotion @emotion/core @emotion/styled

As usual, we need to add some settings:

// gatsby-settings.js
module.exports = {
  // more settings here...
  plugins: [
    `gatsby-plugin-emotion`,
    // more plugins here...
  ],
}

I suspect there is a better way to do this but I could not figure it out. What worked for me was to use that /** @jsx jsx */ hint at the top of the file. Whenever we want to use emotion in our components it will look something like this:

// just an example, you don't have to paste this anywhere right now
/** @jsx jsx */
import { jsx } from '@emotion/core'

export default function SomeComponent(props) {
  return (
    <div css={{color: 'red}}>Hi</div>
  )
}

You can learn more about emotion here.

Add markdown based blog

Now we get to the meaty parts. In order to have a blog where the content comes from Markdown files, we need to install a bunch of plugins.

  • gatsby-source-filesystem will read files from a given folder and make sure that they can be queried by a GraphQL query in our components.
  • gatsby-transformer-remark takes a Markdown file and turns it into HTML.
  • gatsby-remark-images processes images in our Markdown content and makes them responsive by saving different sizes for our production build.
  • gatsby-remark-responsive-iframe wraps iframes in your Markdown content in a responsive container with a fixed aspect ratio.
  • gatsby-remark-prismjs adds syntax highlighting to code blocks in our Markdown content.
  • gatsby-remark-component allows us to use React components in Markdown. This is especially useful so that we can use the <OutboundLink> component that comes with the Google Analytics plugin.
cd ~/Projects/YOURBLOGNAME/YOURBLOGNAME
npm install --save gatsby-source-filesystem
npm install --save gatsby-transformer-remark
npm install --save gatsby-remark-images gatsby-plugin-sharp
npm install --save gatsby-remark-responsive-iframe
npm install --save gatsby-transformer-remark gatsby-remark-prismjs prismjs
npm install --save gatsby-transformer-remark gatsby-remark-component
npm install --save rehype-react

Now we need to add two settings objects, one for gastby-source-filesystem and one for gatsby-transformer-remark. The latter has a "plugins" key itself which means that we are adding most of the plugins as sub-pluggins to the gatsby-transformer-remark plugin (that's a lot of plugins in one sentence o_O):

// gatsby-settings.js
module.exports = {
  // more settings here...
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/posts/`,
        name: 'markdown-pages',
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 750,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1rem`,
            },
          },
          {
            resolve: 'gatsby-remark-prismjs',
          },
        ],
      },
    },
    // more plugins here...
  ],
}

We need to do more! Add this one line to gatsby-browser.js. This loads the theme for the syntax highlighting. You can find out about available Prism themes here.

// gatsby-browser.js
require('prismjs/themes/prism-okaidia.css')

Next we have to perform a bit of black magic. Add this code to gatsby-node.js:

// gatsby-node.js
const path = require('path')

exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions

  const blogPostTemplate = path.resolve(`src/templates/blogPost.js`)

  return graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              path
            }
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      return Promise.reject(result.errors)
    }

    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.frontmatter.path,
        component: blogPostTemplate,
        context: {}, // additional data can be passed via context
      })
    })
  })
}

As far as I understand it, we can define a createPages function. When Gatbsy tries to render all static pages, it will call this function. The function will query a GraphQL endpoint which was magically provided by the gatsby-transformer-remark plugin and call the createPage action for each item in the queryset.

If we ever want to change which files will be rendered (i.e. exclude posts that you have marked as drafts), we would have to change the query here.

Finally, it is in this file where we define which template should be used to render a blog post (src/templates/blogPost.js).

Since we just referenced the file src/templates/blogPost.js, let's create that file:

// src/templates/blogPost.js
/** @jsx jsx */
import React from 'react'
import rehypeReact from 'rehype-react'
import { graphql } from 'gatsby'
import { jsx } from '@emotion/core'
import { OutboundLink } from 'gatsby-plugin-google-analytics'

import Layout from '../components/layout.js'
import SEO from '../components/seo.js'

// this is some black magic to make sure that the OutboundLink component can
// be used in Markdown content as <outbound-link href="..."></outbound-link>
const renderAst = new rehypeReact({
  createElement: React.createElement,
  components: { 'outbound-link': OutboundLink },
}).Compiler

export default function Template({
  data, // this prop will be injected by the GraphQL query below.
}) {
  const { markdownRemark } = data // data.markdownRemark holds our post data
  const { frontmatter, htmlAst } = markdownRemark

  return (
    <Layout>
      <SEO title={frontmatter.title} description={frontmatter.description} />
      <h1 css={{ marginBottom: '0px' }}>{frontmatter.title}</h1>
      <p>
        <small>
          <i>
            {frontmatter.date} &raquo; {markdownRemark.timeToRead} min read
          </i>
        </small>
      </p>
      {renderAst(htmlAst)}
    </Layout>
  )
}

// the fields in this GraphQL query will be available in the React component
// above
export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      htmlAst
      timeToRead
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        title
        description
      }
    }
  }
`

Now it's time to add a Markdown file and make sure that we are able to see it in the browser. Create a file src/posts/hello-world.md:

# src/posts/hello-world.md

---

type: 'blog'
path: '/hello-world/'
date: '2018-12-24'
title: 'Hello World'
description: 'Just a test post.'

---

Hello world! Merry Christmas, everyone!

The things at the top of the file are called "frontmatter". You can come up with any keys and values here and you will be able to query these fields in your GraphQL queries. I like to add a key type to all my posts so that I can later create different posts for different pages, i.e. post for my blog, post for book reviews and posts for boardgame reviews. All files will be found in the src/posts folder, but I will be able to create different GraphQL queries and filter by the type value.

he last piece in the puzzle is to update our index.js file and make sure that it displays a list of all published blog posts:

// replace index.js content this this:
/** @jsx jsx */
import { Link } from 'gatsby'
import { jsx } from '@emotion/core'

import { rhythm, scale } from '../utils/typography.js'
import Layout from '../components/layout.js'
import SEO from '../components/seo.js'

export default function IndexPage(props) {
  const { edges: posts } = props.data.allMarkdownRemark
  return (
    <Layout center={0}>
      <SEO title="My blog" description="My cool blog" />
      <div
        css={{
          '@media(min-width: 721px)': { marginLeft: rhythm(1) },
        }}
      >
        <h1 css={{ ...scale(1 / 4) }}>Articles</h1>
        {posts
          .filter(post => post.node.frontmatter.title.length > 0)
          .map(({ node: post }) => {
            return (
              <div css={{ marginBottom: rhythm(1 / 4) }} key={post.id}>
                -{' '}
                <Link to={post.frontmatter.path}>{post.frontmatter.title}</Link>
              </div>
            )
          })}
      </div>
    </Layout>
  )
}

// here we can see how to filter by the `type` value which is part of the
// frontmatter in our markdown files
export const pageQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(
      filter: { frontmatter: { type: { eq: "blog" } } }
      sort: { order: DESC, fields: [frontmatter___date] }
    ) {
      edges {
        node {
          id
          frontmatter {
            path
            date(formatString: "MMMM DD, YYYY")
            title
          }
        }
      }
    }
  }
`

Let's run gatsby develop and have a look at our new blog!

Add some global styles

At this stage, we will want to apply some finishing touches to our site. We might want to change the colors of headings, body-text and links. This can be achieved with the <Global> component that comes with emotion. Let's add this to layout.js because that file is imported in all our other files, so this is a great place to put things that we want to be rendered on every page:

/** @jsx jsx */
import React from 'react'
import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby'
import { Global, jsx } from '@emotion/core'

import { rhythm } from '../utils/typography.js'
import Header from './header'

const linkColor = '#1F5B77'
const headingColor = '#4A4A4A'

const Layout = ({ children }) => (
  <StaticQuery
    query={graphql`
      query SiteTitleQuery {
        site {
          siteMetadata {
            title
          }
        }
      }
    `}
    render={data => (
      <React.Fragment>
        <Global
          styles={{
            h1: { color: headingColor },
            h2: { color: headingColor },
            h3: { color: headingColor },
            a: { color: linkColor, textDecoration: 'none' },
            'a:hover': { borderBottom: `1px solid ${linkColor}` },
            '.gatsby-highlight': {
              marginBottom: rhythm(1),
            },
          }}
        />
        <Header siteTitle={data.site.siteMetadata.title} />
        <div
          style={{
            margin: `0 auto`,
            maxWidth: 960,
            padding: `0px 1.0875rem 1.45rem`,
            paddingTop: 0,
          }}
        >
          {children}
        </div>
      </React.Fragment>
    )}
  />
)

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout

Host on netlify

Lastly, we will want to put our new blog online. I can highly recommend netlify for this. It is so simple, I will not even describe how it works, just sign up for an account and click at the most obvious links and you will have your site online in a matter of minutes. If you need some guidance, check out the official step by step guide by netlify which I mentioned at the beginning of this article.

Lastly, I made sure that my domain is hooked up with netlify. This is of course different for each DNS provider, so you will have to figure that out yourself. In my case, I was using Namecheap and I simply changed the DNS servers for my domain to the netlify DNS servers and waited for a few hours.

Once that change has propagated, you will also be able to activate HTTPs via the netlify web interface.


Thank you for reading this post. I hope it proved to be useful for you. If you would like to buy me a Chai tea latte, you can send a few satoshis to my BTC address: 3Q6dfwHh594vkNs2HqXk9YsqBSFK4VzuJ5

If this post motivated you to build your own blog, please feel free to post a link to your blog in the comments!