Headless WordPress: Why Gatsby Should Be Next on Your List of Things to Learn

#

Like a lot of developers, I’m always paying attention to what the latest ‘new shiny’ thing in web development is. I’ve heard a lot about Gatsby.js lately, so I started to look into it. It turns out that Gatsby is a great way to create a static site using React.js. On top of that, one of Gatsby’s killer features is that it can pull in content from many different sources, including your WordPress site.

In this post I’ll go over what Gatsby is and why you should think about it for your next WordPress project.

What is Gatsby

In simplest terms, Gatsby is a static site generator. Static site generators are tools to create a bunch of fully styled HTML pages without having to write all the HTML yourself. Think of them as tools to create an old-school website but without having to do it all by hand. The benefit of static sites is that they’re fast (no backend code), secure (no authentication or plugins) and generally easier to maintain. Build the files, deploy – done.

While static site generators like Jekyll have been around for years, the newer group of generators allow you to specify content ‘sources’ – like WordPress. The gist is that you can have a WordPress backend for creating content and a static site ‘frontend’. You may have heard of ‘headless WordPress‘ – this is essentially the definition. WordPress powers the content creation and the static site generator handles the front-end.

Where Gatsby does this differently is by using some bleeding-edge technology to create each site. Under the hood, Gatsby uses standard React for templating, but uses GraphQL for retrieving data. GraphQL is a very new method for querying data and is meant as a replacement for REST APIs (😱). Where Gatsby is different is that instead of rendering React components at runtime, HTML templates are created ahead of time using a Node.js process.

I found though, that setting up and using Gatsby (if you’re familiar with React) was actually fairly simple. Though new, Gatsby has a fairly robust plugin community. There is also no shortage of tutorials and documentation on how to use WordPress as your content provider.

Getting set up

Note: If you’re not super familiar with React, don’t worry it’s fairly straightforward. That said, some examples will require a bit of documentation reading 😃

There’s a few things you need to get set up with Gatsby and WordPress. Gatsby has a good overview of what’s needed with their step zero documentation. I’m going to use the examples used on the Gatsby website as I had the most success following these guides.

Also, if you’re looking for a fully finished version I’ve deployed my static site code to Github.

The first thing you need is a WordPress install which we’ll connect to later (I’m assuming you have one of those 😃. Don’t have one yet? Spin one up in minutes using SpinupWP). Next up you need to get the gatsby-cli tool. The Gatsby Quickstart gives you a good overview of how to get a new Gatsby site up and running.

Something I wish I knew before I started working away is that there are many ‘starters’ for Gatsby. These are essentially themes and predefined settings for your Gatsby site. You can see a listing of all the starters in the starter library, there’s even a couple pre-made for WordPress, but I’m going to go with the basic gatsby-starter-blog as I’m porting my own blog to Gatsby.

 gatsby new my-new-site gatsbyjs/gatsby-starter-blog

Once Gatsby is done creating the site, you can cd my-new-site and run gatsby develop. This will get everything running at http://localhost:8000.

Gatsby starter site screenshot

At this point you’ll have a fully functioning Gatsby site. Let’s load up the site code in our editor and up next we’ll see how to grab content from your WordPress site.

Data Sources

The guts of how Gatsby works is by using source plugins to get data for your site. This is one of the key selling points of using Gatsby over another static site generator or even CMS:

Data in Gatsby sites can come from anywhere: APIs, databases, CMSs, local files, etc.

Source plugins fetch data from their source. E.g. the filesystem source plugin knows how to fetch data from the file system. The WordPress plugin knows how to fetch data from the WordPress API.

In our case, we’re looking to pull data in from our WordPress site so we’re going to use the gatsby-source-wordpress plugin. You can add it like any other yarn/npm module: yarn add gatsby-source-wordpress;

Once it’s installed, we’re going to add some configuration values to the gatsby-config.js file in the root of our site. We add the configuration to the ‘plugins’ array there. The documentation for the gatsby-source-wordpress plugin shows what configuration options are required, but it’s basically the baseUrl, protocol and searchAndReplaceContentUrls fields that are the most important.

plugins: [
 /*
 * Gatsby's data processing layer begins with “source”
 * plugins. Here the site sources its data from WordPress.
 */
{
  resolve: "gatsby-source-wordpress",
  options: {
    /*
     * The base URL of the WordPress site without the trailing slash and the protocol. This is required.
     * Example : 'gatsbyjsexamplewordpress.wordpress.com' or 'www.example-site.com'
     */
    baseUrl: process.env.GATSBY_WP_URL,
    // The protocol. This can be http or https.
    protocol: process.env.GATSBY_WP_PROTOCOL,
    // If your site is hosted on wordpress.org, then set this to false.
    hostingWPCOM: false,
    // If useACF is true, then the source plugin will try to import the WordPress ACF Plugin contents.
    // This feature is untested for sites hosted on wordpress.com.
    // Defaults to true.
    useACF: false,
    verboseOutput: false,
    // Set how many pages are retrieved per API request.
    perPage: 100,
    // Search and Replace Urls across WordPress content.
    searchAndReplaceContentUrls: {
      sourceUrl: `${process.env.GATSBY_WP_PROTOCOL}://${process.env.GATSBY_WP_URL}`,
      replacementUrl: process.env.GATSBY_REPLACEMENT_URL,
    },
    // Set how many simultaneous requests are sent at once.
    concurrentRequests: 10,
    includedRoutes: [
      "**/categories",
      "**/posts",
      "**/pages",
      "**/media",
      "**/tags",
      "**/taxonomies",
      "**/users",
    ],
  },
}]

I’m using the dotenv package to use environment variables for the baseUrl so that I can customize which WordPress site to pull content from based on environment variables. You could also hardcode these values.

//.env.development
GATSBY_WP_URL=petetasker.com
GATSBY_WP_PROTOCOL=https
GATSBY_REPLACEMENT_URL=http://localhost:8000

Once the config is updated we’ll restart Gatsby so that it pulls in our WordPress data into our local GraphQL instance…

Excuse me reaction gif

GraphQL for the masses

You may have heard the term GraphQL floating around lately. It’s the latest new-shiny and is meant to be a replacement of sorts for REST APIs. For our purposes with Gatsby we don’t really need to know everything about GraphQL as Gatsby abstracts a lot of it away. However, it’s good to know that we’ll be using a query-like system to get data for our components.

GraphQL is fairly straightforward, you essentially ask for the data you want. With a REST API you hit an endpoint and you get back whatever that endpoint returns. This can lead to over and/or under fetching. Multiple requests are required to get the data you’re looking for, and you either get too much data or not enough. With GraphQL you ask the GraphQL server for the exact data you’re looking for in one request.

Additionally, the format of your request is the format of the response because of a standard schema and type system shared between server and client.

For example, say you have a GraphQL query that looks like this:

{
    wordpressPost(wordpress_id: { eq: 682 }) {
        title
        content
        excerpt
        date(formatString: "MMMM DD, YYYY")
        author {
                name
        }
    }
}

That would return the following JSON-looking blob:

GraphiQL screenshot JSON

Gatsby includes a fun tool called GraphiQL running on http://localhost:8000/___graphql. This is like PHPMyAdmin for GraphQL. You can try out your queries there, which we’ll definitely need to do in the next step.

Converting WordPress data to Gatsby pages

Remember that gatsby-config.js file? Well there’s another file in the root called gatsby-node.js. This is where we transform our WordPress data into Gatsby pages.

In this file we’ll add some GraphQL queries and a little Node.js magic to transform our WordPress content into Gatsby posts and pages. I found this tutorial by Tim Smith to be a great overview of this process.

In gatsby-node.js we’ll use the createPages method to add our WordPress posts and pages we query from GraphQL.

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.createPages = async ({ graphql, actions, reporter }) => {

const { createPage } = actions

const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
const PageTemplate = path.resolve("./src/templates/Page.js")

const result = await graphql(`
    {
    allWordpressPost {
        edges {
        node {
            slug
            wordpress_id
        }
        }
    }
    allWordpressPage {
        edges {
        node {
            slug
            wordpress_id
        }
        }
    }
    }
`)

if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
}

const BlogPosts = result.data.allWordpressPost.edges

BlogPosts.forEach(post => {
    createPage({
        path: `/post/${post.node.slug}`,
        component: BlogPostTemplate,
        context: {
            id: post.node.wordpress_id,
        },
    })
})

const Pages = result.data.allWordpressPage.edges

Pages.forEach(post => {
    createPage({
        path: `/${post.node.slug}`,
        component: PageTemplate,
        context: {
            id: post.node.wordpress_id,
        },
    })
})
}

What the above does is leverage the ‘createPages’ method to create our site’s pages. It does this by querying GraphQL for the WordPress data:

const result = await graphql(`
    {
        allWordpressPost {
            edges {
                node {
                    slug
                    wordpress_id
                }
            }
        }
        allWordpressPage {
            edges {
                node {
                    slug
                    wordpress_id
                }
            }
        }
    }
`)

Then, we use the templates we’ll create in a minute and populate them with the createPage function:

const BlogPostTemplate = path.resolve("./src/templates/BlogPost.js")
const PageTemplate = path.resolve("./src/templates/Page.js")

...

const BlogPosts = result.data.allWordpressPost.edges

BlogPosts.forEach(post => {
    createPage({
        path: `/post/${post.node.slug}`,
        component: BlogPostTemplate,
        context: {
            id: post.node.wordpress_id,
        },
    })
})

const Pages = result.data.allWordpressPage.edges

Pages.forEach(post => {
    createPage({
        path: `/${post.node.slug}`,
        component: PageTemplate,
        context: {
            id: post.node.wordpress_id,
        },
    })
})

Before we restart Gatsby, we’ll create the template files it needs to create these pages. In src/templates/BlogPost.js you can add something like this (the starter should already have something similar):

import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const BlogPostTemplate = ({ data }) => {
    const postData = data.wordpressPost
    let featuredImg = undefined

    if (postData.featured_media) {
        featuredImg = postData.featured_media.localFile.childImageSharp.fixed
    }

    return (
        <Layout>
            <SEO title={postData.title} description={postData.excerpt} />
            {featuredImg && <Img fixed={featuredImg} />}
            <h3 style={{ fontSize: 33, marginTop:0 }} dangerouslySetInnerHTML={{ __html: postData.title }} />
            <div dangerouslySetInnerHTML={{ __html: postData.content }} />
        </Layout>
    )
}
export default BlogPostTemplate

export const query = graphql`
    query($id: Int!) {
        wordpressPost(wordpress_id: { eq: $id }) {
            title
            content
            excerpt
            date(formatString: "MMMM DD, YYYY")
            author {
                name
            }
            featured_media {
                localFile {
                childImageSharp {
                    fixed(width: 1000) {
                    ...GatsbyImageSharpFixed
                    }
                }
                }
            }
        }
}
`

I won’t get too far into the weeds here as Tim does a great job in his tutorial. We basically have a React component called BlogPostTemplate and a GraphQL query that describes what data we want in our template. The output of the GraphQL query is injected as a React ‘prop’ so we can use it in our component.

You’ll also see that GraphQL can take arguments, in this case we’re using the wordpress_id key to get the data for a specific post.

As in our gatsby-node.js file, we can access the data from our GraphQL query in the same format it was fetched. The title of the post is available at data.wordpressPost.title and so forth.

If you run gatsby develop again you should see that you have posts and pages created. Since we haven’t created a post ‘listing’ page yet, we can use Tim’s handy hack by going to a page that doesn’t exist, triggering a 404, and seeing the listing of posts there.

404 page post listing

Whoop! And with that we’ve got the basics of our site going. If you’d like to create a listing of posts on your home page, you just need to map over a list of posts and output some JSX.

In src/pages/index.js you can do something like:

<Layout>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
    <ul style={{ listStyle: "none" }}>
    {data.allWordpressPost.edges.map(post => (
        <li key={post.node.wordpress_id}>
        <Link to={`/post/${post.node.slug}`}>
        </Link>
            <div>
                <Link
                    to={`/post/${post.node.slug}`}
                    style={{
                    // display: "flex",
                    color: "black",
                    textDecoration: "none",
                    }}
                > <h3
                    dangerouslySetInnerHTML={{ __html: post.node.title }}
                    style={{ fontSize: 33, marginTop:0 }}
                /></Link>
                <p style={{ margin: 0, color: "grey", fontSize:16, marginTop:8, marginBottom:10 }}>
                    Written by {post.node.author.name} on {post.node.date}
                </p>
                <div dangerouslySetInnerHTML={{ __html: post.node.excerpt }} />
            </div>
        </li>
    ))}
    </ul>
</Layout>

With a GraphQL query of:

export const query = graphql`
query {
    allWordpressPost {
    edges {
        node {
        title
        content
        slug
        excerpt
        wordpress_id
        author {
            name
        }
        date(formatString: "MMMM DD, YYYY")
        featured_media {
            localFile {
            childImageSharp {
                fixed(width: 1000) {
                ...GatsbyImageSharpFixed
                }
            }
            }
        }
        }
    }
    }
}
`

Deployment

Assuming all went well above, you’ve got a fully fleshed out Gatsby site that’s displaying your WordPress content. The last thing to do is deploy that sucker.

Luckily, one of the great things about static sites is that they’re super simple to deploy (as they’re just plain HTML and CSS). Netlify takes it a step further however.

With Netlify you can add the GitHub repo for the site and have it deploy and host your site for you.

Netlify options screenshot

And that’s all there is to it. Netlify lets you point your own DNS for free, so my static site is up at static.petetasker.com. Cool right? The code for the site can be found on Github here.

Netlify also provides webhook URLs to trigger deployments. You could take things a step further by triggering a Netlify deployment whenever a post is published by adding something like the following to a plugin on your WordPress site:

add_action('publish_post', function(){
    wp_remote_post('https://api.netlify.com/build_hooks/your-id', []);
});

Final thoughts

In this post I just barely covered some of the things Gatsby can do. It’s really a full application framework for creating websites, more akin to a full-stack framework like Laravel or Ruby on Rails. If you’re interested, I highly recommend going through the tutorials on the Gatsby website as they go into more depth on how it all works together.

All that said, is it worth creating a static site for your WordPress-based content? As with everything, it depends. Before Gatsby, simply having a static site didn’t make sense for a lot of people because you’d lose out on the dynamic nature of something like WordPress.

With Gatsby you can pull in content from multiple sources making a dynamically-backed static site. I think that’s a powerful combination. However, I agree with this post on the Gatsby blog, use Gatsby if:

  1. You or your developers don’t want to build a WordPress theme
  2. You want to be on the bleeding edge
  3. You want a fast site without having to set up caching

What do you think about static site generators like Gatsby? How about ‘headless WordPress’?

Let us know below!

About the Author

Peter Tasker

Peter is a PHP and JavaScript developer from Ottawa, Ontario, Canada. In a previous life he worked for marketing and public relations agencies. Love's WordPress, dislikes FTP.