Gatsby is all the rage right now. It is a blazing fast React-based static site generator and has won the hearts of many. Developers and end-users love the experience it brings.

The product produced when using Gatsby is a nimble, super secure, low-resource static site composed of HTML, CSS and JavaScript files. When a visitor visits a Gatsby site, they are visiting a static HTML page, which correlates with improved user experience, higher SEO rankings, lower hosting costs and improved site security.

Gatsby sites still need to get their content from somewhere though. There are many Gatsby source plugins that allow you to source content from various formats ranging from markdown files, to data managed in a CMS such as WordPress.

In this article, we will use WordPress, the Open Source Content Management System that powers a third of the web, as our source of content for a Gatsby site. Typically WordPress is used for managing content and rendering that content in a WordPress theme.

We are going to use WordPress to manage the content, but use Gatsby to render it. This allows us to take advantage of the speed and security benefits of Gatsby and the power, familiarity and customizability of WordPress as the CMS.

Gatsby, similar to WordPress, has its own plugins, with which you can extend Gatsby a great deal. For example, you can have support for SASS to style the site, Apollo GraphQL for fetching dynamic data, Google Analytics tracking and Algolia search.

Gatsby has introduced themes this year and I couldn’t be more excited. With the launch of themes, Gatsby is poised to give other static site generators sleepless nights.

Theming with Gatsby

With themes in Gatsby, you can essentially add styling as just another npm module. You define a single theme or an array of themes within your site’s `gatsby-config.js` file and then get a beautiful site with almost zero effort. Gatsby also has support for parent/child themes for you to extend a theme and add customizations, similar to WordPress but more advanced.

Gatsby themes allow you to focus only on the parts of the site and app building process that you need to care about by abstracting the rest away into a package.

Jason Lengstorf

With the introduction of themes, you can now create a site composed of content in Markdown files or content from a CMS, for example. All the heavy-lifting such as sourcing and transformation of the content can be concealed within the parent or child theme. This is great news for developers and agencies who do not have tech-savvy clients. Just install a Gatsby theme and let content creators create.

The best part is, you can have a site which has a Blog, Docs and eCommerce and have three different themes with a common color scheme – what a bonus!

Gatsby WordPress Themes

Educator Zac Gordon, has built a team to work on Gatsby themes for WordPress at GatsbyWPThemes.com.

As part of the project, we are converting popular WordPress Themes, in cooperation with their creators, into Gatsby themes. We have learned a lot from this project, and the rest of this write-up is about how we ported the popular Twenty Nineteen WordPress Theme over to Gatsby and how you too can create your own Gatsby themes.

You can see a demo of the Twenty Nineteen Gatsby Theme in action here.

Note: This has no connection to WordPress themes except for getting their styles. You don’t have to create these themes within your WordPress site’s `wp-content/themes` directory. It is a completely separate set up.

Getting started

To help you make the best use of this tutorial, I am assuming the following:

  • You have a basic understanding of React and SASS
  • You have a basic understanding of WordPress

You will need node and npm to follow along with this tutorial. This is the official site to download node on your computer.

With that out of the way, let’s get started. I’m initializing the repository the way Jason Lengstorf and John Otander do it on their livestream.

These are the steps that Jason and John follow:

1. Create a new Theme Directory

First, we will create a new directory and change the current working directory into it. This directory is completely independent of your WordPress installation, and therefore should be outside your WordPress root folder:

mkdir twentynineteen-gatsby-theme
cd twentynineteen-gatsby-theme

2. Create Package Manager file

We will `npm init` within that directory to create a `package.json` file

npm init

Follow the prompts and enter the details, while each time pressing the enter key (what you enter won’t matter since it will be replaced in a moment). This command will create a package.json file inside a given directory. It is essentially a way to initialize a folder to act as an npm (node package manager) module so that it can be packaged later on.

Then we will use that to create a new yarn workspace, so that we can start building the theme as a package and then have another directory for the end user’s site. Workspaces allow you to setup multiple packages in such a way that you only need to run yarn install once to install all of them in a single pass.

So, go to the `package.json` file inside the root folder and make it look like this:

{
 "private": true,
 "name": "twentynineteen-gatsby-theme",
 "version": "1.0.0",
 "workspaces": [
   "site",
   "packages/*"
 ]
}

`site` refers to the Gatsby site and `packages` will contain our theme.

Create two folders within the root directory:

  1. site
  2. packages

Navigate to the site directory and `npm init` within it.

Now, open up the `package.json` file within `site` and replace the contents of it with this:

{
 "name": "site",
 "version": "0.1.0",
 "description": "The Site to test the Twenty Nineteen Gatsby Theme.",
 "main": "gatsby-config.js",
 "license": "MIT",
 "scripts": {
   "build": "gatsby build",
   "develop": "gatsby develop",
   "start": "npm run develop",
   "serve": "gatsby serve",
   "test": "echo \"Write tests! -> https://gatsby.app/unit-testing\""
 },
 "dependencies": {
   "gatsby": "^2.1.23",
   "react": "^16.8.3",
   "react-dom": "^16.8.3",
   "gatsby-theme-twentynineteen": "*"
 }
}

The scripts will allow us to spin up a Gatsby development server or build the Gatsby site. Notice the new dependency in the list “gatsby-theme-twentynineteen”. We will create it next.

Next, head over to the `packages` folder and create a new directory naming it `gatsby-theme-twentynineteen`. Enter this directory and `npm init` within it, just like before.

The best way (according to John Otander) to have Gatsby, React and ReactDOM together is to install them as a development dependency and a peer dependency.

So, the finished package.json file within `packages/gatsby-theme-twentynineteen` should look like this:

{
 "name": "gatsby-theme-twentynineteen",
 "version": "0.1.0",
 "description": "The Gatsby Theme - ported from Twenty Nineteen WordPress theme.",
 "main": "index.js",
 "license": "MIT",
 "devDependencies": {
   "gatsby": "^2.1.23",
   "react": "^16.8.3",
   "react-dom": "^16.8.3"
 },
 "peerDependencies": {
   "gatsby": "^2.1.23",
   "react": "^16.8.3",
   "react-dom": "^16.8.3"
 }
}

3. Install dependencies

Now, navigate to the root folder (twentynineteen-gatsby-theme) and run the command `yarn`. This will install all dependencies needed for our workspace, since we did not use the `yarn install` command before.

If you don’t have yarn on your computer, install it using `npm install –global yarn`

4. Hook the theme to the site

So, how do we manage the dependencies for the theme and site? The best way is to create the theme as a dependency for the site. You would have already done that if you followed the instructions mentioned above.

How do we tell the site to use the npm module as the theme? Well, we do this inside the site’s `gatsby-config.js` file as follows:

module.exports = {
 siteMetadata: {
   title: `Twenty Nineteen Site`,
   description: `Gatsby site for Twenty Nineteen Gatsby Theme.`,
   author: `@muhsinlk`,
   wordPressUrl: `http://dev-twenty-nineteen-gatsby-demo.pantheonsite.io/`
 },
 __experimentalThemes: [
   {
     resolve: "gatsby-theme-twentynineteen",
     options: { wordPressUrl: `http://dev-twenty-nineteen-gatsby-demo.pantheonsite.io/` }
   }
 ]
};

We are passing the WordPress site’s URL as an option to the theme, which we will access later. Also, the siteMetaData will be useful later on.

This is how you would set up the project for creating a Gatsby Theme and linking it to a Gatsby Site. To continue developing the site, enter:

yarn workspace site develop

You can continue developing a Gatsby Theme within the theme folder. Since walking through the process of building an entire theme (e.g. Twenty Nineteen Gatsby Theme) will be too long for a tutorial, we will clone the project and look at how the project was built by examining its files and folders.

Cloning the project from GitHub

The Twenty Nineteen Gatsby Theme project just finished V1 and is hosted on GitHub (https://github.com/zgordon/twentynineteen-gatsby-theme). We will first clone it to our localhost and try running it.

# Clone the repo to any directory, away from WordPress root directory
git clone https://github.com/zgordon/twentynineteen-gatsby-theme.git

# Move into the new directory
cd twentynineteen-gatsby-theme

# Install dependencies
yarn

# Start the site on http://localhost:8000
yarn start

You can now open the `twentynineteen-gatsby-theme` directory with your favorite code editor. What follows is an explanation of the code in the project and the reasons for doing things the way we did it.

Setting up gatsby-source-graphql

Gatsby has APIs for creating static pages out of content fetched remotely. For this example we fetched data from a WordPress backend via GraphQL. To be precise, we used the WPGraphQL plugin created by the brilliant Jason Bahl, who also worked with us on this project.

To those who are unfamiliar with GraphQL, it is a way of fetching data from an endpoint and may replace REST APIs in the near future. It creates a schema of your backend data in a graph-like format. It will allow you to query data by only asking for the properties that you need. This is in contrast to REST API which returns all fields related to an endpoint. GraphQL will have a single endpoint and you can query data in all sorts of ways – even from multiple objects or tables.

We used Gatsby’s `gatsby-source-graphql` plugin to fetch data from the WPGraphQL endpoint. For this, you should have installed WPGraphQL plugin on your WordPress site.

If you open the theme’s `gatsby-config.js` file, you will see this:

module.exports = ({ wordPressUrl }) => ({
 plugins: [
   {
     resolve: `gatsby-source-graphql`,
     options: {
       // This type will contain remote schema Query type
       typeName: `WPGraphQL`,
       // This is field under which it's accessible
       fieldName: `wpgraphql`,
       // Url to query from
       url: `${wordPressUrl}/graphql`,
     },
   },
   `gatsby-plugin-sass`,
 ],
})

This config fetches the option we passed to the theme from the site’s gatsby-config file earlier, and sets it as the url for fetching the GraphQL data. Now, Gatsby will pull in data from the endpoint and and create a stitched schema in the local build. Schema stitching is the process of creating a single GraphQL schema from multiple underlying GraphQL APIs. We can query from this static GraphQL schema, but of course we can’t mutate to it since it is not a live endpoint. The other `gatsby-plugin-sass` plugin is for telling Gatsby that we will have SASS files.

Creating static pages with Gatsby’s createPage API

We aimed to replicate the WordPress Twenty Nineteen Theme. This meant recreating all pages from the WordPress template – be it the posts index archive, single post, single page, tag archive, category archive and author archive. We had to query the data via GraphQL and create those pages statically.

Gatsby has a few neat APIs to help us create static pages. All of this is done inside the `gatsby-node.js` file. To keep things tidy, we separated the static page creation into different files based on the type of page.

Our theme’s `gatsby-node.js` should look like this:

const createPosts = require(`./utils/createPosts`)
const createPages = require(`./utils/createPages`)
const createUsers = require(`./utils/createUsers`)
const createCategories = require(`./utils/createCategories`)
const createTags = require(`./utils/createTags`)

exports.createPages = async ({ actions, graphql }) => {
 await createPosts({ actions, graphql })
 await createPages({ actions, graphql })
 await createUsers({ actions, graphql })
 await createCategories({ actions, graphql })
 await createTags({ actions, graphql })
}

If you open the utils folder, you will see the following files within it:

  1. createPosts.js – creates single post and posts list archive
  2. createPages.js – creates single pages
  3. createUsers.js – creates author archive
  4. createCategories.js – creates category archive
  5. createTags.js – creates tag archive

The createPages API is part of the Node APIs that Gatsby exposes. It essentially instructs Gatsby to add pages. Within this we are calling some methods using async/await (a feature of ECMAScript 2017). All five of those functions do a similar task – create relevant static pages. Since explaining all of them is redundant, let’s look at what createPosts does.

This is a breakdown of what createPosts function does:

  • Import a GraphQL fragment and JSX templates
  • Define GraphQL Query
  • Export default a function which does the actual page creation
  • Define a function as a property (fetchPosts) which does the following:
    • Query for posts from the static GraphQL endpoint and fetch them
    • Define properties for blogPages array to create the blog archive pages
    • Add all returned posts to an allPosts array
    • Check for more posts and fetch them in a recursive call
  • Call the above defined function and get the results
  • Create static pages for the archive and individual posts using the createPage Gatsby Node API
  • Send nodes (posts) and single post as page context for archive and single templates respectively

I highly recommend you check out these files yourself. We have added comments for everything so even a beginner to Gatsby can understand the role of these functions. Here is the /utils/createPosts.js file:

const { PostTemplateFragment } = require(`../src/templates/posts/data.js`)
const postTemplate = require.resolve(`../src/templates/posts/single.js`)
const blogTemplate = require.resolve(`../src/templates/posts/archive.js`)

const GET_POSTS = `
  # Define our query variables
  query GET_POSTS($first:Int $after:String) {
    wpgraphql {
      # Ask for posts
      posts(
          # Ask for the first XX number of posts
          first: $first 
          
          # A Cursor to where in the dataset our query should start
          # and get items _after_ that point
          after:$after
      ) {
          # In response, we'll want pageInfo so we know if we need
          # to fetch more posts or not.
          pageInfo {
              # If true, we need to ask for more data.
              hasNextPage
              
              # This cursor will be used for the value for $after
              # if we need to ask for more data
              endCursor
          } 
          nodes {
              uri
              
              # This is the fragment used for the Post Template
              ...PostTemplateFragment
          }
      }
  }
  }
  # Here we make use of the imported fragments which are referenced above
  ${PostTemplateFragment}
`

/**
 * Array to store allPosts. We make paginated requests
 * to WordPress to get allPosts, and once we have all posts,
 * then we iterate over them to create pages.
 *
 * @type {Array}
 */
const allPosts = []

/**
 * Here we store an array of blogPages. For each xx amount of posts
 * we want to create a blogPage so users can browse
 * chunks of data at a time, much like a traditional
 * WordPress paginated archive page.
 *
 * @type {Array}
 */
const blogPages = []

/**
 * We need to track the page number so we can output the paginated
 * archive template with the appropriate path.
 *
 * @type {number}
 */
let pageNumber = 0

/**
 * This is the export which Gatbsy will use to process.
 *
 * @param { actions, graphql }
 * @returns {Promise<void>}
 */
module.exports = async ({ actions, graphql }) => {
  /**
   * This is the method from Gatsby that we're going
   * to use to create pages in our static site.
   */
  const { createPage } = actions

  const fetchPosts = async variables => {
    /**
     * Fetch posts using the GET_POSTS query and the variables passed in.
     */
    return await graphql(GET_POSTS, variables).then(({ data }) => {
      /**
       * Extract the data from the GraphQL query results
       */
      const {
        wpgraphql: {
          posts: {
            nodes,
            pageInfo: { hasNextPage, endCursor },
          },
        },
      } = data

      /**
       * Define the path for the paginated blog page.
       * This is the url the page will live at
       * @type {string}
       */
      const blogPagePath = !variables.after ? `/` : `/page/${pageNumber + 1}`

      /**
       * The IDs of the posts which were got from GraphQL.
       */
      const nodeIds = nodes.map(node => node.postId)

      /**
       * Add config for the blogPage to the blogPage array
       * for creating later
       *
       * @type {{path: string, component: string, context: {nodes: *, pageNumber: number, hasNextPage: *}}}
       */
      blogPages[pageNumber] = {
        path: blogPagePath,
        component: blogTemplate,
        context: {
          ids: nodeIds,
          nodes,
          pageNumber: pageNumber + 1,
          hasNextPage,
        },
      }

      /**
       * Map over the posts for later creation
       */
      nodes &&
        nodes.map(post => {
          allPosts.push(post)
        })

      /**
       * If there's another page, fetch more
       * so we can have all the data we need.
       */
      if (hasNextPage) {
        pageNumber++
        console.log(`fetch page ${pageNumber} of posts...`)
        return fetchPosts({ first: 10, after: endCursor })
      }

      /**
       * Once we're done, return all the posts
       * so we can create the necessary pages with
       * all the data on hand.
       */
      return allPosts
    })
  }

  /**
   * Kick off our `fetchPosts` method which will get us all
   * the posts we need to create individual post pages
   * and paginated blogroll archive pages.
   */
  await fetchPosts({ first: 10, after: null }).then(allPosts => {
    /**
     * Map over the allPosts array to create the
     * single-post pages
     */
    allPosts &&
      allPosts.map((post, index) => {
        console.log(`create post: ${post.uri}`)
        createPage({
          path: `/blog/${post.uri}/`,
          component: postTemplate,
          context: {
            ...post,
            prev: allPosts[index + 1],
            next: allPosts[index - 1],
          },
        })
      })

    /**
     * Map over the `blogPages` array to create the
     * paginated blogroll pages
     */
    blogPages &&
      blogPages.map(archivePage => {
        console.log(`createBlogPage ${archivePage.context.pageNumber}`)
        createPage(archivePage)
      })
  })
}

Let’s explore a couple of template files

So, we saw in the last section that we imported two templates at the top of createPosts.js. Let’s look at the files one by one!

The /src/templates/posts/single.js file is responsible for showing a single post and does the following:

  • Import relevant libraries: React and Gatsby Link
  • Import Layout (like a master template for using across pages: see more here), SEO (for managing SEO using React Helmet), and two Metadata components from a relative path
  • Define SinglePost as a classless functional React component
  • Extract pageContext (post info) from props (this comes from the createPage API from the createPosts.js file) and destructure the properties
  • Return JSX that show a single post

The /src/templates/posts/archive.js file is responsible for iterating through a list of posts and showing it as an archive does the following:

  • Import relevant js files
  • Define renderPreviousLink function
  • Define renderNextLink function
  • Define BlogArchive as a stateless functional React component

Also, there is a data.js component inside the posts folder. This has a GraphQL fragment which is used when creating the static pages in createPosts file.

More React Components

We are using many React components inside our project. Here is a list of each file and what it does:

  1. layout.js – contains the Layout component (master) which was described earlier
  2. header.js – the Header component used within Layout
  3. footer.js – the Footer component used within Layout
  4. menu.js – the Primary Menu used on the site
  5. seo.js – contains the SEO component which uses React Helmet to set meta tags for Search Engine Optimizations
  6. post-entry.js – the file used within archive.js and more to iterate through posts
  7. recent-posts-widget.js – contains RecentPostsWidget component used in the Footer
  8. categories-widget.js – contains CategoriesWidget component used in Footer
  9. post-header-meta.js – a reusable component for displaying post meta in the post’s header
  10. post-footer-meta.js – reusable component for displaying post meta in the post’s footer
  11. icons folder – a collection of SVG icons exported as React Components

Bringing in the styles

Since we were porting over the Twenty Nineteen WordPress theme, we copied over all its styles. Twenty Nineteen uses Sass for managing styles, so we needed two npm modules responsible for handling .scss files:

  1. gatsby-plugin-sass
  2. node-sass

These two modules are installed and configured in `gatsby-config.js` as shown before.

As soon as I copied the scss files, I saw some errors when I ran `yarn workspace site develop`. After some troubleshooting, it turned out the issue was with using vw units as value for the flex property.

The problem was with the `sass/site/primary/_comments.scss` file. I change the following styles accordingly:

flex: 1 0 calc(3 * (100vw / 12));
/* to: */
flex: 1 0 25%;
/* and */
flex: 0 0 calc(2 * (100vw / 12));
/* to: */
flex: 0 0 16.67%;

And everything started working as expected.

You can now run the following command:

yarn workspace site develop

Your site will now look like this:

Twenty Nineteen Gatsby Theme
Twenty Nineteen Gatsby Theme


Gotchas

Here are some of the issues you may come across as you continue development and their fixes:

  • If your menu location name is something other than Primary (MENU_1), you might have to change the GraphQL query within components/menu.js to match yours. Use the GraphiQL IDE to get the identifier of your menu location.
  • If you are using StaticQuery within the theme, your theme folder (the one within packages directory) name and package name should start with `gatsby-theme-`. Otherwise StaticQuery component will not render. Chris Biscardi is working on a PR to remove this restriction.

TL;DR;

Thanks for reading this far. We did not cover line by line all of the JSX and templates, however we covered the aspects of getting started on building a theme.

To summarise we covered the following in this tutorial:

  • Create a theme and site within a yarn workspace
  • Connect the theme to the site by adding it as a dependency of the site and in the gatsby-config file
  • Set up `gatsby-source-graphql` to fetch data from WPGraphQL
  • Fetching data with GraphQL
  • Creating static pages with Gatsby’s createPage Node API
  • Templating with JSX
  • Styling

Hopefully this is enough for you to get going playing around with this theme or even building your own!

Related resources

For more information on this, you can refer to the following resources regarding Gatsby Themes:

WordPress has seen a beautiful journey from the days theme development relied on PHP and the template hierarchy, to today, with Gatsby arriving on the scene.

Now is a great time to jump in to the WordPress-Gatsby world and see the magic for yourself.

Do join us on this journey, and let us know your feedback.

Happy coding!