Build and Host your Own Blog with Gatsby and GitHub Pages for Free
TLDR: Nowadays, it's becoming harder and harder to stand out from the crowd. If you want to launch your own online presence and show off your tech skills as someone who is starting out in the tech space, then building your own custom blog with
Gatsby.js
could be a great way to do both. You could also deploy your blog (for free!) to GitHub Pages.If you follow this article to the end, you'll have a professional blog site deployed to the web with a homepage that shows a list of article previews and a page for each article. New pages will automatically be generated when you add new content!
Prerequisites: You'll need some experience with npm and React, and a very basic understanding of the concepts behind GraphQL. You'll also need to be familiar with git and GitHub.
Git commits: initial commit > configure Gatsby plugins > add an article > create components > homepage query > generate pages dynamically > blog post template > add the deployment script.
FYI: my new blog uses NextJS 13 and Notion as a CMS 👌
Introduction
Gatsby can serve static React pages, but can also generate static pages from React templates that you create. To add a new page to our site we will just need to write a new markdown (
.md
) file. Gatsby will then fill in the template from our content and generate the page for us - this is what makes the framework so powerful and widely-used for creating a blog.We will add metadata to our
.md
files by adding a "frontmatter", where we can define any information (in yaml
syntax) we want, such as the title, date, slug, and author. A frontmatter is prepended to the article .md
file like so:
--- title: "A blog article" some_other_property: some-value --- ...
Gatsby uses GraphQL syntax to query the markdown files in our filesystem and pass through the blog content and any metadata to our template as a prop.
Here's what we're going to do in this article:
- Generate a new Gatsby project in a new git repo connected to a GitHub repo.
- Configure Gatsby with the plugins we're going to need for parsing the markdown into HTML.
- Add our first article markdown file.
- Understand how Gatsby uses GraphQL and write our first query for page content and metadata.
- Dynamically generate the blog pages from our markdown files.
- Create a template to display the blog post pages.
- Deploy our project to GitHub Pages.
Generate a new Gatsby project
To generate a new Gatsby project, run:
npm init gatsby
Name your project "
blog
" and put it in a folder of the same name. Don't add any CMS (No (or I'll add it later)
). I also chose to install styled-components
to use in my project as a styling library. When given the option Would you like to install additional features with other plugins?
navigate straight to (Done)
.Once your project is generated, open it up in you IDE of choice - you can run
npm run develop
inside the project directory to serve a hot-reloading development server on http://localhost:8000
.Let's initialise a git repo for the project. You'll also need to create a remote repo on GitHub where we're going to deploy our blog.
# (cd blog) git init git remote add origin <your-remote-repo-url-goes-here>
Let's configure all the plugins we're going to need and then we'll write some basic components for our site.
Configure Gatsby plugins
We're going to install some plugins to use in our project which will add some extra functionality to our Gatsby site:
# These libraries parse our markdown into HTML (including images and code blocks). npm i gatsby-transformer-remark gatsby-remark-images gatsby-remark-prismjs prismjs gatsby-remark-highlight-code # This library allows you to query the data from your markdown files (e.g., the titles of articles). npm i gatsby-source-filesystem # These libraries allow us to render image components. npm i gatsby-plugin-image gatsby-transformer-sharp gatsby-plugin-sharp # This library will allow us to add a manifest (in particular, a favicon) to our site. npm i gatsby-plugin-manifest
For these plugins to take effect, we need to declare them in
gatsby-config.js
in the root of the project. We can add the plugins as string values to the plugins
attribute of the module.export
, or we can specify options and sub-plugins by adding an object to the plugins
array and resolving it by name. You can add as many as you want to add extra functionality to your website.// gatsby-config.js module.exports = { siteMetadata: { ... }, plugins: [ 'plugin-name', ... { resolve: 'plugin-name', options: { plugins: [ /* more plugins here */ ] } } ] }
Have a look at the
gatsby-config.js
in my project here. I also required a small extra commit to set up my code block formatting - you can find that hereNow that we've configured the plugins we're going to be using, we can create some basic components to use in our project.
src/pages/index.js
will be our homepage where we'll see the blog previews. I'll let you create a page that you're happy with here (I'm using styled-components
and blueprint.js
for styling and basic components, but use what you're comfortable with). Here're the changes I made.Next, we'll also want to create a component to display the preview of each blog article - our homepage will render some small cards with a preview image and title. Create a
src/components
directory and create a new card-like component for this:// src/components/BlogPreviewCard/BlogPreviewCard.jsx import { navigate } from 'gatsby' import { GatsbyImage, getImage } from 'gatsby-plugin-image' export const BlogPreviewCard = ({ title, slug, thumbnail, thumbnail_alt, author, minsToRead, datePublished, }) => { const navigateToPost = () => navigate(slug) return ( <StyledCard onClick={navigateToPost}> <GatsbyImage image={getImage(thumbnail)} /> {/* Write some code here that uses the props and returns a card component */} </StyledCard> ) }
We will pass the correct props through to the component later. The important thing to understand is that the
slug
is the location of the markdown file you want to show and when we navigate to this address we see the blog post.To render images in your React code, you can use
gatsby-plugin-image
.You can find the preview card I made here.
Write your first article
Create a new file called
src/blog/article-one/index.md
and add the metadata as below:--- title: "Article One" slug: blog/article-one thumbnail: "./thumbnail.png" thumbnail_alt: "thumbnail description" author: "Your name" date: 2021-08-01 --- <the-body-of-your-article-goes-here>
You will also need to add in a new file called
src/blog/article-one/thumbnail.png
which will be a thumbnail image for your article.Understanding Gatsby and GraphQL
Gatsby allows you to use GraphQL to query site data, however Gatsby abstracts some of the boilerplate away for us, so we need to do our queries in a particular way. We are going to have two main queries on our site: one for our homepage, where we query all the metadata and thumbnails of our articles; and one on our blog post page to get the content of the blog post.
In both of these components we need to export a
graphql
query for the page. In the background, when generating the pages, Gatsby runs the query and injects the result into the page component (exported as default
). This gets passed in as the data
prop.import { graphql } from 'gatsby'; const SomeComponent = ({ data }) => { ... } export default SomeComponent; export const query = graphql` // Quey goes here `;
If you're looking to find the schema for your Gatsby site, you can go to http://localhost:8000/___graphql to play around with the sandbox and find what the queries should look like. You can then copy and paste the correct query that you want to pass into your component into your code.
Let's return to
index.js
(if you've already added something there) and add a query to the page which will grab all the information we need to show our blog summaries:// src/pages/index.js import { graphql } from 'gatsby'; import { BlogPreviewCard } from '../components/BlogPreviewCard/BlogPreviewCard' const IndexPage = ({ data: { allMarkdownRemark: { edges } } }) => ( <SiteLayout> ... {edges.map(({ node: { frontmatter: { slug, ..., thumbnail }, timeToRead } }) => ( <BlogPreviewCard slug={slug} ... thumbnail={thumbnail} /> ))} </SiteLayout> ) export default IndexPage export const query = graphql` query { allMarkdownRemark { edges { node { timeToRead frontmatter { slug title date author thumbnail_alt thumbnail { childImageSharp { gatsbyImageData( width: 800 placeholder: BLURRED formats: [AUTO, WEBP, AVIF] ) } } } } } } } `
Now Gatsby is querying data from all our
MarkdownRemark
s and passing the information through to the IndexPage
to show all our blog previews.Generating pages from markdown files
As Gatsby serves static websites, all the pages that we want to generate dynamically get created at build-time. Gatsby will run any script held within the
gatsby-node.js
file on build of the project - this is where we will tell Gatsby to generate our pages.// gatsby-node.js const path = require(`path`) exports.createPages = async ({ graphql, actions: { createPage } }) => { const { data: { allMarkdownRemark: { edges } } } = await graphql(` { allMarkdownRemark(sort: { fields: [frontmatter___date], order: ASC }) { edges { node { frontmatter { slug } } } } } `) edges.forEach( ({ node: { frontmatter: { slug } } }) => { createPage({ path: slug, component: path.resolve(`./src/templates/BlogPost/BlogPost.jsx`), context: { slug } }) } ) }
We'll now go on to create the actual template component for our actual blog post page that we've used to create a page from.
Create our blog post template
This will be very simple as we'll mostly be showing some parsed HTML. Create a
src/templates/BlogPost/BlogPost.jsx
file and create a BlogPost
component. I've included a snippet below, but you can make this however you like.// src/templates/BlogPost/BlogPost.jsx import { StaticImage } from 'gatsby-plugin-image' const BlogPost = ({ data: { markdownRemark: { html } }} ) => ( <SiteLayout> ... <div dangerouslySetInnerHTML={{ __html: html }} /> </SiteLayout> ) export default BlogPost // You can also grab any extra fields from the frontmatter that you need export const query = graphql` query ($slug: String!) { markdownRemark(frontmatter: { slug: { eq: $slug } }) { html frontmatter { ... } } } `
The important thing to understand here is that Gatsby is using the
gatsby-transformer-remark
plugin to transform our markdown file into HTML, which is being passed to our template component and rendered using dangerouslySetInnerHTML={{ __html: html }}
.We now have a main page which queries all the markdown files and displays a preview for each one based on the metadata. When we click on the card, it navigates us to the correct blog post. We also have a page being generated for each blog post which we've written.
Run
npm run develop
to see your site locally and check that you can see a blog preview which links to your new article.Deploy to GitHub Pages
We will be using the
gh-pages
library to help us deploy to GitHub Pages.npm install gh-pages --save-dev
Let's add a deploy script to our
package.json
which we can run every time we want to publish our site.{ ... "scripts": { "deploy": "gatsby clean && gatsby build --prefix-paths && gh-pages -d public -b gh-pages", ... }, "dependencies": { ... } }
We can now deploy whenever we want by running
npm run deploy
.This script clears the project cache and creates a new build. In my
gatsby-config.js
, I have a path parameter set pathPrefix: '/blog'
which sets the path prefix. The script then builds with gh-pages
, taking from the public
directory that gatsby build
created and pushes the result to a branch on the remote called gh-pages
.We can now go to our remote repo on GitHub and should see a new
gh-pages
branch. Now all we need to do is configure the pages site of the repo. This is done by doing to Settings > Pages
in our repo and setting the Source
to Branch: gh-pages
, /(root)
.After a few minutes your site will be deployed!
Summary
After following this article, you should have learned:
- How to generate a new Gatsby project.
- How to add and configure Gatsby plugins to customise your site.
- How to query Gatsby site data through GraphQL.
- How to dynamically generate pages from a template.
- How to deploy a Gatsby project to GitHub Pages.
You can use this knowledge to add more pages and plugins to customise the blog to however you like.
If you want a quick-start to your project, you can clone my GitHub repo and customise the styling to your liking. If you don't want to clone the repo but just explore the code, go to the repo and press "
.
" when you're on the website - this will open up a VS Code window to view the code.