I spent a few days last week rebuilding my blog with Gatsby. My last blog was built on Ghost, but the upgrade process was such a pain that I never bothered upgrading, and after a few years on their platform it just seemed more simple to manage a single repo instead of having to deal with deployment. Gatsby is a React-based static site generator. It uses some nice things like the latest Javascript, GraphQL and allows to easily add some plugins that add functionality like compressing and generating image sizes easy without the developing having to do too much. In this tutorial I assume some Javascript and React knowledge as well as basic command line knowledge
Migrating from other blogs
The first thing I had to do was migrate my posts over from my other blogs. This step is not painless, but ultimately you only have to do it once and then you can control your data however you want.
From Wordpress
For Wordpress you can use their built in exporter to get an XML dump and then if you are on unix you should be able to clone this repo and run it with node. It takes your xml (hardcoded to export.xml) in the same folder and then outputs a folder of markdown files and downloads the images for you.
https://github.com/jefflau/wordpress-to-markdown
From Ghost
Ghost allows you to export you files as a JSON dump, which you can then convert with something like this: https://github.com/hswolff/ghost-to-md. I could not find a tool to download all the images so I did this manually, but I’m sure you could write a tool to do this too, but I didn’t have that many images. I also manually added a ’/’ to all the slugs which seems to be required in Gatsby.
##Setting up
Installing Gatsby is a breeze, you just need to add the gatsby-cli
with npm install -g gatsby-cli
. Once you do that you’re going to want to create a new app with:
$ gatsby new projectName
This will create a basic skeleton for you to start creating your app.
We will add a bunch of dependencies which I will explain later:
$ npm install --save gatsby-plugin-sharp gatsby-remark-copy-linked-files gatsby-remark-images gatsby-transformer-remark gatsby-transformer-sharp sharp
##Setting up your Markdown files
For me I keep my posts in /src/posts
and in there Gatsby expects a folder for each post. The name of these are unimportant for the sake of Gatsby, but for your sanity I recommend dating them and adding the title so you can find a post for editing later. Inside the folder you an also name your .md
what you like. We’ll also keep our images in the same folder as our post and use some Gatsby magic to link them together.
Now that we have our posts in the correct place we can add some code in /gatsby-config.js
which should have been created when we ran gatsby new
. Here we need to place some code that tells Gatsby to look for our posts and allows other things to access them.
// gatsby-config.js
plugins: [
//... other plugins
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/posts`,
name: 'posts'
}
},
]
Now that we’ve done that we need to tell Gatsby to take those markdown files and make a page for each in /gatsby-node.js
which is another file that gets added on our scaffold step:
// gatsby-node.js
const path = require('path')
exports.createPages = ({ boundActionCreators, graphql}) => {
const { createPage } = boundActionCreators
const postTemplate = path.resolve('src/templates/post.js')
const posts = graphql(`{
allMarkdownRemark {
edges {
node {
html
id
frontmatter {
title
slug
}
}
}
}
}`)
.then(res => {
if(res.errors) {
return Promise.reject(res.errors)
}
res.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.slug,
component: postTemplate,
})
})
})
}
This uses one of Gatsby’s node’s API that run to create our static pages for each markdown file. If you don’t know anything about GraphQL don’t worry about that, but what we’re doing is querying our markdown files using GraphQL, which returns a promise. From that we can take that data and then call the createPage
action creator (Gatsby uses redux in the background).
Finally we need to create the actual post template which we used in gatsby-node.js
. Create a post.js
in /src/templates
and add this code:
import React from 'react'
import Helmet from 'react-helmet'
export default ({data}) => {
const { markdownRemark: post } = data;
return <div>
<div className="date">{post.frontmatter.date}</div>
<h1>
{post.frontmatter.title}
</h1>
<div dangerouslySetInnerHTML={{ __html: post.html}} />
</div>
}
export const query = graphql`
query BlogPostBySlug($path: String!) {
markdownRemark(frontmatter: { slug: { eq: $path } }) {
html
frontmatter {
slug
title
date(formatString: "Do MMMM , YYYY")
}
}
}
`
If you’re familiar with react this will look pretty similar to a normal react component. The only difference is the graphql query at the end. The graphql
function gets injected by gatsby so we don’t need to import it ourselves. This export will attach this query to the props of our component. As you can see we are destructuring the data
property out of this React component’s props
. The data
is the prop that Gatsby uses to attach your markdown information to your template. This is a pretty basic template that just outputs the html and adds the date
and title
from the frontmatter
of your markdown post. The frontmatter
is the terminology used by Gatsby that represents all the meta information of your posts kept at the top between the ---
triple hyphens.
From this we can already query each of our posts by going to localhost:8000/blog-post-slug.
##Creating the blog post listing page
Now that we have all the markdown files being made into static html files and a template to show them, we need to create a listing page to show all our blog posts. We could make this any page such as /blog
, but I am just going to show it on the index of the blog just for simplicity. Paste this code into your index.js
file over your current index file in your /src/pages
folder.
// index.js
import React from 'react'
import Link from 'gatsby-link'
const IndexPage = (props) => {
const { data: { allPosts: { edges: posts} } } = props
return <section className="posts-container">
{posts.map(({ node }) =>
<li key={node.id} className="posts-item">
<Link to={node.frontmatter.slug}>
<span className="posts-title">{node.frontmatter.title}</span>
<span className="posts-date">{node.frontmatter.date}</span>
</Link>
</li>)}
</section>
}
export default IndexPage
export const query = graphql`
query allPosts{
allPosts:allMarkdownRemark(
limit: 1000,
sort: { fields: [frontmatter___date], order:DESC}
){
edges{
node{
id
frontmatter{
title
slug
date(formatString: "D MMM YYYY")
}
}
}
}
}
`
This is similar to the post.js
template. It is a React component with a GraphQL query. Because it lives inside the /pages
folder, Gatsby will automatically make it a route on your blog. Since it’s the index, it will just be the root route of your application. The query is about as simple as it gets. We are querying the allMarkdownRemark
which allows us to query all the markdown from our posts
folder. We are renaming it with an alias allPosts
that makes more sense to our query, this will be the variable that is made available to your on your data
prop. And then we are giving the query some parameters, related to how many blog posts we want (limited to 1000) and we want it sorted by date. GraphQL is out of scope of this blogpost, but there are many other ways to sort and filter your data with this powerful query language.
Lastly we are making sure we have the title, slug and date from each post and we are formatting the date in a certain way that Gatsby’s version of GraphQL allows us to do.
Once we’ve done that we should have a live blog! I deployed my blog using surge which is pretty much a oneliner by just running:
$ npm install -g surge
$ gatsby build
$ cd public
$ surge . --domain somedomain.surge.sh
I keep this as an npm script in package.json:
"scripts": {
"build": "gatsby build",
//...other scripts
"deploy": "gatsby build && cd public && surge . --domain jefflau.net",
}
And there you have it, you should have a fully functioning, but basic blog running with Gatsby! There is much more you can do with Gatsby such as adding tags and categories, dynamic navigation of your pages, pretty much anything you can do with your Wordpress blog, and it’s super easy to deploy!