In my previous article last week, I mentioned creating a partially ported WordPress-Gatsby site. This article is a continuation with a step-by-step walkthrough under the hood.
Gatsby, a React-based framework for static sites, is attracting attention not only from JavaScript developers but also from the WordPress developers and users. Many WordPress users find its features appealing, like ultra fast image handling and improved security protection from hackers, but would like to use them while continuing to use the WordPress admin and editor to manage content.
Chris has covered the idea of combining Gatsby and WordPress before here on CSS-Tricks. As a WordPress enthusiast, I decided to give it a try. This article is based on what I learned and documented along the way.
Please note that WPGraphQL and gatsby-cli are in constant development with breaking changes in later versions. This project was done using WPGraphQL 0.8.3, gatsby-source-wpgraphql 2.5.1 and gatsby-cli 2.12.21. Unlike WordPress, newer WPGraphQL releases do not support backward compatibility. Please consult the official WPGraphQL & Gatsby doc for the latest changes and proceed with caution before using.
There are ready-to-use projects in the Gatsby starters library. Two great examples are Alexandra Spalato’s gatsby-wordpress-theme-blog and the twenty-nineteen-gatsby-theme by Zac Gordon and Muhammad Muhsin.
Prerequisites
If you want to follow along, here’s what you’ll need:
Basic familiarity of React and JavaScript. Here are getting started guides for React and JavaScript.Basic understanding of Gatsby and how dynamic pages are created. Here is a link to an excellent step-by-step tutorial guide to learn Gatsby.Familiarity with WordPress and a working installation. Here are guides to help get you started.
Assets and resources
Because I had already done a few Gatsby learning projects in the past, I had some assets like typography, layouts, and other reusable components that I could apply here. I had also gone through the following recent tutorial guides, which helped me to prepare for this project.
Overview – Guide to Gatsby WordPress Starter Advanced with Previews, i18n and more by Henrik WirthMigrate Your WordPress Site to the Jamstack by Jason LenstorfPorting the Twenty Nineteen WordPress Theme to Gatsby by Muhammad Muhsin
Henrick Wirth’s guide is super comprehension and thorough. Jason’s step-by-step article is a great resource and even includes super helpful videos that help see the process take place. Muhammad’s article helps explain how static pages are created with Gatsby’s createPages API and breaks down various functions, template files and React components along the way.
I largely followed Henrik’s guide and divided this article into similar sections. Henrik’s guide includes image handling and adding PageBuilder with ACF Flexible Content features which we don’t get into here.
Article Sections:
Setting up WordPress and GatsbyPorting posts and pages from WordPressWorking with navigationDisplaying blog posts in GatsbyStyling and deployment
Section 1: Setting up WordPress and Gatsby
First, let’s set up a WordPress site for a data source. This could be an already existing site or a fresh one. Even a local WordPress installation is fine. I decided to start with a new test WordPress site for this project using the Twenty Twenty theme that ships with WordPress.
Install the WPGraphQL and WPGraphiQL plugins
Let’s start by installing a couple of plugins in WordPress. We’ll use WPGraphQL to enable GraphQL API in WordPress and open up WordPress as a data source. We’ll also use WPGraphiQL (note the “i” in the name). This one is actually optional, but it creates an interface for testing GraphQL queries directly in the WordPress dashboard, which is super handy. You may notice that I’m linking to the GitHub repos for the plugins instead of the WordPress Plugin Directory and that’s intentional — neither plugin is available in the directory at the time of this writing. As such, you’ll download the ZIP files and manually install them in WordPress via the /wp-content/plugins directory.
Once activated, the GraphiQL API is displayed in the WordPress dashboard.
The GraphiQL API provides a playground to test GraphQL queries from WordPress site. The GraphiQL screen provides three panels: one to navigate between different objects (left), one to query data (center),and one to visualize the returned data (right).
Setting up a local Gatsby site
We will setup a local Gatsby site by installing Gatsby’s starter default in the wordpress-gatsby directory of the project with this in the command line:
#! create a new Gatsby site using the default starter
gatsby new wordpress-gatsby https://github.com/gatsbyjs/gatsby-starter-default
Restart the server with gatsby develop, then let’s navigate to localhost:8000 in a new browser tab. We should get a starter page in the browser.
A link to how to create a gatsby site locally is available from the Gatsby documentation.
Next, we’re going to install and configure the gatsby-source-graphql plugin. Just as we did when setting up WordPress, we have to install and configure WPGraphQL in the Gatsby site.
#! install wpgraphql plugin
#! add with yarn
yarn add gatsby-source-graphql
#! install with npm
npm install –save gatsby-source-graphql
OK, now it’s time to configure the gatsby-source-graphql plugin. Open up the gatsby-config.js file and let’s use these settings:
// plugin configuration
module.exports = {
plugins: [
{
resolve: gatsby-source-graphql,
options: {
typeName: WPGraphQL,
fieldName: wpcontent,
// GraphQL endpoint, relative to your WordPress home URL.
url: https://tinjurewp.com/wp-gatsby/graphql,
// GraphQL endpoint using env variable
// url: ${process.env.WORDPRESS_URL}/graphql,
},
},
],
}
How did I come up with this exact configuration? I strictly followed what’s described in the Gatsby docs. The plugin was added to the Gatsby instance by specifying the URL of the GraphQL endpoint (highlighted above) and two configuration options: typeName, a remote schema query type, and fieldName, which is available in the Gatsby query. Please note, the latest WPGraphQL doc suggest using fieldName: wpcontent instead of wpgraphqlas described in the guide.
Alternative setup: Use the dotenv module
Optionally, we could have set things up using the dotenv npm module to define environment variables that are used to customize the development environment. Henrik uses this method in his guide as well.
If you’re using this method, a variable in the .env.production plugin configuration file, like WORDPRESS_URL, can be defined and used instead of exposing the WordPress URL.
# .env.production
# Dont put any sensible data here!!!
WORDPRESS_URL=https://tinjurewp.com/wp-gatsby/
My test environment equally exposes the WordPress instance and data to WPGraphQL.
Colby Fayock has a helpful step-by-step guide on using environmental variables with Gatsby and Netlify.
After re-starting the development server, the WPGraphQL API is available with Gatsby to query and retrieve the specific data that’s queried from the WordPress site and display it on a Gatsby site through the localhost GraphQL URL at https//localhost:8000/___graphql/. Note that, unlike in WordPress site itself, the data here is exposed to WPGraphQL. We can query against the WPGraphQL API to display any field from the WordPress site.
Section 2: Porting posts and pages from WordPress
In Gatsby, posts and pages can be created at build time by querying data with GraphQL and mapping the query results to posts or page templates. The process is described in a Gatsby tutorial on programmatically creating pages from data. Gatsby make use of two APIs, onCreateNode and createPages, and tutorial contains a detailed explanation on how they are implemented.
The code snippets here come from Henrik’s guide. Because of the way WordPress stores data in its database under different data types and categories, porting all the contents turns out to be less than straightforward. However, with prior knowledge of creating pages and posts with Gatsby createPages API and Node API, I was able to follow along. There’s also a lot of real-world starter sites that can be referenced as examples.
Step 1: Add posts and pages content in a WordPress site
Add some posts and pages in WordPress site if you don’t have any already. Before creating page for that content, we need to delete index.js and page-2.js from the pages folder of the Gatsby site. These two files seem to interfere with the ported WordPress data.
Step 2: Create page and post template
We’re going to create two template files for our content, one for posts (/src/templates/posts/index.js) and one for pages (/src/templates/pages/index.js).
Here’s our post template. Basically, we’re using the post title twice (one as the SEO page title and one as the post heading) and the post content as a Post component.
// src/templates/post/index.js
import React from react
import Layout from ../../components/layout
import SEO from ../../components/SEO
?
const Post = ({ pageContext }) => {
const post = pageContext.post
?
return (
?
{post.title}
?
)
}
?
export default Post
We’ll do nearly the same thing for the page template:
//src/templates/pages/index.js
import React from react
import Layout from ../../components/layout
import SEO from ../../components/seo
?
const Page = ({ pageContext }) => {
const page = pageContext.page
?
return (
?
{page.title}
?
)
}
?
export default Page
Step 3: Create static posts and pages with the createPages API
Note that the entire code we’re covering here can be written in the node.js file. However, for readability purposes, posts and pages are separated in a folder named create in the project’s root directory following Henrik’s Guide.
We’re going to get our hands dirty with the GraphQL createPages API! We’ll start by adding the following to gatsby-node.js.
// gatsby-node.js
const createPages = require(./create/createPages)
const createPosts = require(./create/createPosts)
?
exports.createPagesStatefully = async ({ graphql, actions, reporter }, options) => {
await createPages({ actions, graphql, reporter }, options)
await createPosts({ actions, graphql, reporter }, options)
}
Muhammad’s post makes a good point that’s worth calling out here:
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).
In other words: both functions create relevant static pages. With that in mind, let’s define what data we want to use and fetch that data in the create/createPages.js file. Sorry for the big code dump, but Henrik’s comments help explain what’s happening.
//create/createPages.js
const pageTemplate = require.resolve(../src/templates/page/index.js);
?
const GET_PAGES = `
query GET_PAGES($first:Int $after:String) {
wpgraphql {
pages(
first: $first
after: $after
# This will make sure to only get the parent nodes and no children
where: {
parent: null
}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
pageId
content
uri
isFrontPage
}
}
}
}
`
?
const allPages = []
let pageNumber = 0
const itemsPerPage = 10
?
/** This is the export which Gatbsy will use to process.
* @param { actions, graphql }
* @returns {Promise} */
module.exports = async ({ actions, graphql, reporter }, options) => {
?
/** This is the method from Gatsby that were going
* to use to create pages in our static site. */
const { createPage } = actions
/** Fetch pages method. This accepts variables to alter
* the query. The variable `first` controls how many items to
* request per fetch and the `after` controls where to start in
* the dataset.
* @param variables
* @returns {Promise<*>} */
const fetchPages = async (variables) =>
/** Fetch pages using the GET_PAGES query and the variables passed in. */
await graphql(GET_PAGES, variables).then(({ data }) => {
/** Extract the data from the GraphQL query results */
const {
wpgraphql: {
pages: {
nodes,
pageInfo: { hasNextPage, endCursor },
},
},
} = data
?
/** Map over the pages for later creation */
nodes
&& nodes.map((pages) => {
allPages.push(pages)
})
?
/** If theres another page, fetch more
* so we can have all the data we need. */
if (hasNextPage) {
pageNumber++
reporter.info(`fetch page ${pageNumber} of pages…`)
return fetchPages({ first: itemsPerPage, after: endCursor })
}
?
/** Once were done, return all the pages
* so we can create the necessary pages with
* all the data on hand. */
return allPages
})
?
/** Kick off our `fetchPages` method which will get us all
* the pages we need to create individual pages. */
await fetchPages({ first: itemsPerPage, after: null }).then((wpPages) => {
?
wpPages && wpPages.map((page) => {
let pagePath = `${page.uri}`
?
/** If the page is the front page, the page path should not be the uri,
* but the root path /. */
if(page.isFrontPage) {
pagePath = /
}
?
createPage({
path: pagePath,
component: pageTemplate,
context: {
page: page,
},
})
?
reporter.info(`page created: ${page.uri}`)
})
?
reporter.info(`# —–> PAGES TOTAL: ${wpPages.length}`)
})
}
Again, Muhammad’s post is excellent help because it breaks down what the createPages.js and createPosts.js functions can do. Henrik’s guide also provides helpful comments for each step.
Step 4: Creating posts
The createPosts.js file is almost identical to createPages.js. The sole difference is prefixing the path with blog/ and replacing the “page” with “posts” throughout the code.
If we stop here and restart the development server with gatsby develop in the terminal, the develop log displays the page buildup.
Now, if we open up localhost:8000 in a browser, we get a 404 error.
That might be off-putting, but it’s all good. Clicking any of the links on the 404 page displays the correct page or post from the WordPress data source. For example, if the sample-page link is clicked, it displays sample page content from WordPress in the browser.
Section 3: Working with navigation
Let’s move on to the navigation menu for our site. WordPress has a navigation management feature that allows us to construct menus using links to pages, posts, archives, taxonomies, and even custom links. We want to create navigation for a main menu in WordPress and send it to GraphQL where we can query it for our own site.
Navigation links — including page and post links — are created in Gatsby using the Gatsby Link API, which uses both the built-in component and navigate function. The component is used for linking to internal pages, but not to external links.
Porting navigation menu from the WordPress into Gatsby site turns out to be a tricky little task that requires creating