An introduction to Gatsby for WordPress Developers
For a long time, my personal website was a built on WordPress. Iāve worked with WordPress a bunch in my career and felt like it was a good balance of functionality and flexibility. But lately, Iāve thought about ditching it all and switching over to a static site. I personally love writing in Markdown, and the new WordPress editor relegated Markdown writing to a second class citizen. So I figured now was the time to switch over to something else entirely different, something like Gatsby.
Gatsby is a static site generator, if youāre not familiar, that allows you write your templates in React and uses NodeJS under the hood to compile your site. I enjoyed building my new site: creating templates, configuring the GraphQL queries and getting back into traditional web development.
At work, Iāve written about using WordPress as data source on the SpinupWP blog, and I wanted to know what it would be like to switch from WordPress to a Markdown based blog.
What follows are the steps I followed to migrate my site from a self-hosted WordPress site to a Gatsby site hosted on Netlify. It may not be the exact process that you will need to follow to migrate your own WordPress site, but I think it covers the most common steps.
Extracting Content From WordPress
The first step to getting content out of WordPress was grabbing an XML export. This can be done using the WordPress core exporter. You can run create the export by logging into your wp-admin and going to Tools > Export.
Once you have an export XML file youāll need a markdown converter. There are few available online, I used the wordpress-export-to-markdown script, but thereās plugins and scripts like ExitWP available online that do the same thing.
Itās pretty straightforward to convert the XML export into Markdown. With the wordpress-export-to-markdown script itās really just this one command:
npx wordpress-export-to-markdown --save-attached-images true
After the script ran, I had a folder with a bunch of new markdown files and a folder with my media uploads. I just dumped the markdown files into a āblogā folder and all media into a āblog-post-imagesā folder. You could group each post in a folder with itās media, but I opted for this set up for the old posts to keep them separate.

The data in the Markdown files was a little mangled, but not too bad. The āfrontmatterā (the metadata for each post) was chucked in the header of the Markdown file, so a lot of the work formatting the files was removing this junk.
For the most part, the posts came across ok. There was a bit of formatting and styling needed in terms of <code> and <pre> tags, as well as fixing up image paths. Other than than, most formatting was in pretty good shape!
Getting Gatsby up and running
Alright, so now weāve got out WordPress content, now what? Welp, the first thing we have to do is get Gatsby up and running. Fortunately this is pretty easy and the Gatsby docs are very helpful.
// Install the gatsby cli tool globally
npm install -g gatsby-cli
// Create a new Gatsby site in a 'gatsby-starter-blog' folder
gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog
I opted to use the Gatsby Starter Blog starter as it already has a lot of the Markdown plugins included, as well as some pretty decent defaults and app structure.
In Gatsby land, starters as pre-built boilerplates, and itās really awesome how far they can get you right out of the box. There are a bunch of options for pretty much any design style you could ask for. Think of starters as a WordPress theme and set of plugins.
Gatsby does have the concept of themes as well, but for most smaller sites a starter is just fine. The only thing you lose by using a starter over a theme is that if the starter is updated down the road youāll have no way to pull in any upstream changes.
For me, thatās a solid āmehā.
Once youāve run gatsby new, youāll have a pretty nice Gatsby app ready to go. If you cd into āgatsby-starter-blogā and run gatsby develop you should see your new blog running at http://localhost:8000. And at this point, if youāve moved your markdown files into the ācontent/blogā folder, they should have been created as Gatsby posts.
Howād that happen?
How Gatsby Works
If youāre coming from WordPress land, the concept of a ācompiledā website might seem a little strange. Thatās what Gatsby does, it compiles a dynamic site (React components and a content source) into a (mostly) static website. Because of this compilation step, most of the magic happens during the build step.
Before we get into the build side of things, itās helpful to see how the content and structure of the site is created.
The first thing to learn about is the gatsby-config.js file. This is where we load in our Gatsby plugins and configuration. For our Markdown files, we use the gatsby-source-filesystem plugin to load them in, specifying the path in the configuration:
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/assets`,
name: `assets`,
},
},The Gatsby starter will have this file mostly populated out of the gate, but itās good to know itās purpose and location.
Gatsby Node APIs
The next thing to learn about are the Gatsby Node APIs. These are managed by the gatsby-node.js file. Here, we define how pages get created and how they interface with the GraphQL layer.
The main function to create pages is the called, unironically, createPages(). In here we define the query to get our posts, and any additional data we want to add to our posts/pages. We then call the createPage() function for each āpostā we want created.
Itās important to note that gatsby-node.js file is essentially just a node script with access to the Gatsby APIs. This is helpful information if youāre debugging during the build process, you can debug the Gatsby build site just as you would any other Node script.
In this file, we import a template to use when the createPage() function is called a bit later.
const blogPost = path.resolve(`./src/templates/blog-post.js`)Then, we have our GraphQL query which is saved in the postsResult variable. We use the graphql function which is part of the Gatsby package;
const postsResult = await graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
type
}
}
}
}
}
`
)allMarkdownRemark is a function thatās part of the gatsby-transformer-remark plugin and is Gatsbyās port of the Remark markdown parser. In the gatsby-config.js file weāve already configured this plugin so it knows where to find our Markdown files.
Gatsby also has a great overview explaining what GraphQL is and why itās so cool.
All we need to know about the above query is that it gets all of our content from our markdown files, sorted by date and limited to 1000.
The neat thing about GraphQL is that it returns data in the same format as we request it. So we can access data in the postsResult variable like we would any other JS object.
So in our query weāre asking for:
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
...
}
}And in the postsResult var:
// Create blog posts pages.
const posts = postsResult.data.allMarkdownRemark.edgesYou can think of GraphQL queries as similar to WordPress custom WP_Query() calls. We specify what we want, and it returns the data.
<?php
$args = array(
'post_status' => 'future',
'meta_query' => array(
array(
'key' => '_thumbnail_id',
'value' => ā,
'compare' => '!='
)
)
);
$slider_posts = new WP_Query($args);
?>
<?php if($slider_posts->have_posts()) : ?>
<div class='slider'>
<?php while($slider_posts->have_posts()) : $slider_posts->the_post() ?>
<div class='slide'>
<?php the_post_thumbnail() ?>
</div>
<?php endwhile ?>
</div>
<?php endif ?>
?>Example of getting posts for a āsliderā
Just like in WordPress, the last thing to do is loop over all the posts and apply our HTML:
const posts = postsResult.data.allMarkdownRemark.edges
posts.forEach((post, index) => {
let previous, next
const { node } = post
// Figure out links for previous and next post
if (node.frontmatter.type === "post") {
previous = index === posts.length - 1 ? null : posts[index + 1].node
}
if (node.frontmatter.type === "post") {
next = index === 0 ? null : posts[index - 1].node
}
//Actually create the page
createPage({
path: post.node.fields.slug, //Post 'slug'
component: blogPost, // Template to render
context: { //Context date for this post/page
slug: post.node.fields.slug,
previous,
next,
},
})
})In a WordPress theme, you would probably just output some HTML inside the loop. In Gatsby, since this is during the build step, you need to explicitly call the createPage() function to create the page on our site.
The createPage() function uses our React component (blogPost.js) as the template. Just like WordPress uses individual theme component files to output parts of our theme, the createPage() function grabs our template and injects the data needed to render out everything.
The blogPost.js template isnāt super complex, itās just a React component with dynamic data passed in.
Iāll defer to the Gatsby docs for explaining how templates work.
Things also differ from the traditional WordPress development workflow when it comes to images.
Image Handling
Weāve seen so far that Gatsby uses GraphQL to query content for our posts, but how are images handled? Images in Gatsby require the gatsby-image plugin.
gatsby-image is a pretty sweet little package. It will take your large images, resize them, strip metadata, lazy load them and use a āSVG blurred placeholderā all in one.
Yeah.
Per the docs, itās basically just installing a couple npm packages and adding some base configuration to your gatsby-config.js file.
Then, you have a few options for how to use the image in your template and your posts.
For markdown, you just use the markdown syntax for images, and use a relative path to the image:
In a component, you can query for an image with a GraphQL like so:
query BioQuery {
avatar: file(absolutePath: { regex: "/profile-pic.png/" }) {
childImageSharp {
fixed(width: 50, height: 50) {
...GatsbyImageSharpFixed
}
}
}
`)Then elsewhere, use the gatsby-image Image component to render it.
<Image
fixed={data.avatar.childImageSharp.fixed}
alt={author.name}
imgStyle={{
borderRadius: `50%`,
}}
/>It seems a lot more complicated than what you would need to do in a WordPress theme, but I find it only slightly more verbose than this:
<img src="<?php echo esc_url( get_theme_mod( 'banner-section-image', '' ) ); ?>" alt="Alt text" >Iād argue that the biggest improvement over WordPress is Gatsbyās image handling. Having the correct sizes created automatically and having them lazy-loaded is a game changer. It requires next to no effort and everything is super performant out of the box.
Ok, so letās review:
- ā Weāve exported our WordPress site content to Markdown
- ā Weāve exported our media
- ā Weāve created a new Gatsby site that loads our markdown files
- ā Weāre loading up our images in posts and our templates
All thatās left is deployment and hosting!
Deployment and Hosting
One of the sticking points with WordPress is finding a decent host. Most managed hosts can get expensive fairly quickly, and shared hosting is a no-go if you want decent performance. You can self host on a virtual server as I did for years, but you have to keep the underlying OS up to date and patch things, modify the firewall etc. etc. etc. (plug: SpinupWP from Delicious Brains mitigates all of these issues š¤©).
Does hosting Gatsby have the same issues? In a word, no.
Because Gatsby compiles down to essentially a static HTML web site, you can host almost anywhere. Thereās no dynamic content, so itās pretty quick right out of the box. Even more, Netlify offers free hosting of Gatsby sites, including Letās Encrypt SSL certificates and custom domains. Thatās where Iām hosting this site and itās the beeās knees.
Iāve also set up git deployments, so pushing to master deploys the site.
Where WordPress is a better option
Ok, so all this sounds pretty great doesnāt it? Well it is, and Gatsby is awesome, but itās not without issues.
Gatsby isnāt a CMS, so none of the CMS nice things are available. Want to handle a contact form? Thatās an external service. Want comments on your blog post? Thatās an external service. Want to sell stuff or have user sessions? Thatās an externalā¦
You get the point.
Itās a static site, so itās a static site. Thereās no dynamic aspect to the site, everything is built at compile time. Thatās probably the biggest drawback of Gatsby, thereās no ādynamicā functionality on your site by default.
Of course, there are workarounds and services that will get you this interactivity, but it involves weaving together third party services, like Disqus for comments or Shopify for ecommerce.
Iāve got Disqus comments enabled (leave a comment!) and use Netlifyās form handling for my contact form. But if youāve got a highly dynamic site with dynamic content, Gatsby is probably a no-go.
WordPress on the other hand is dynamic by default, so you can get pretty far with plugins and custom code.
In the end
For my own purposes, as a developer, Gatsby is a great solution. I can write in Markdown, deploy my site with git push origin main and write React code for my templates.
One more time.
What do you think about Gatsby over WordPress?
You can check out the source for this site on Github