Gatsby and WordPress: Creating Content

Mark Sta Ana - Jan 2 '19 - - Dev Community

Photo by Billy Huynh on Unsplash

Pulling in content from WordPress

Now that we’ve verified that the plugin can pull in data from our WordPress site, let’s start creating static content based on our posts and pages!

Let’s start with something simple and pull in the posts and display them in the developer console. This involves two steps:

  • Create a GraphQL query
  • Act upon the results of the query being run (in this case we’ll just display the contents of the query)

Page creation is handled by createPages API, using promises we can perform the required steps to programmatically create pages. Let’s edit the gatsby-node.js add the following code.

exports.createPages = ({ graphql, actions }) => {
  const createWpPosts = new Promise((resolve, reject) => {
    const query = graphql(`
      {
        allWordpressPost {
          edges {
            node {
              id
              slug
            }
          }
        }
      }
    `)

    query.then(result => {
      console.log(JSON.stringify(result, null, 4))
      resolve()
    }) // query.then
  }) // createWpPosts

  return Promise.all([createWpPosts])
} // createPages
Enter fullscreen mode Exit fullscreen mode

Go ahead and restart gatsby develop, you should see the following in your terminal:

success building schema — 0.920 s
⠁ {
    "data": {
        "allWordpressPost": {
            "edges": [
                {
                    "node": {
                        "id": "51ec1d4e-7b5f-54b8-b5ea-f36aea0c1d8f",
                        "slug": "a-long-form-post"
                    }
                },
Enter fullscreen mode Exit fullscreen mode

Whilst this is not very exciting, it confirms that we’ve been able to pull our posts from WordPress. It turns out the two fields id and slug are just what we need to begin programmatically creating pages.

Create pages based on Wordpress

Let’s update our code, for brevity we've omitted blocks of code that haven’t changed if you get stuck there’s a commit hash at the end of this checkpoint.

const path = require('path');

exports.createPages = ({ graphql, actions }) => {
    const { createPage } = actions

    const createPostPages = new Promise((resolve, reject) => {
      // omitted

      query.then(result => {
        if (result.errors) {
          console.error(results.errors)
          reject(result.error)
        }

        const postEdges = result.data.allWordpressPost.edges

        postEdges.forEach(edge => {
          createPage({
            path: `/${edge.node.slug}`,
            component: path.resolve(`./src/templates/post.js`),
            context: {
              id: edge.node.id,
            },
          })
        })

        resolve()
// omitted
Enter fullscreen mode Exit fullscreen mode

Before we create any pages, let’s do a bit of error handling and return early from our promise if we hit any errors.

Next, we extract our results into a const to make it easier to read the code.

const postEdges = result.data.allWordpressPost.edges
Enter fullscreen mode Exit fullscreen mode

You’ll recall that this is just a flattening of our GraphQL query.

{
  allWordpressPost {
    edges {
        ...
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

We then iterate over our results calling the createPage API, which expects three parameters:

  • path which will be used to create a page slug, WordPress already has this data, so rather than compute it ourselves let’s reuse it.
  • component is React parlance for a unit of markup. Think of a controls toolbox with various components you can add to a form. We’ll take about this in the next section.
  • context allows us to pass data to the component, in this case, we’ll pass the WordPress Id of our posts.

Checkpoint: Create a blog post template

That’s a lot of information to take in, but we’re almost there!

Remember the component parameter from the last section?

component: path.resolve(`./src/templates/post.js`),
Enter fullscreen mode Exit fullscreen mode

Let’s go ahead and create that file post.js.

Note: you’ll need to create a new subdirectory within src called templates.

src/templates/post.js

import React from 'react';
import Layout from '../components/layout';

export default () => {
    return (
        <Layout>
            <div>Hello blog post</div>
        </Layout>
    )
}
Enter fullscreen mode Exit fullscreen mode

Let’s stop and restart Gatsby:gatsby develop

We don’t have an index page to view our newly created post, but the Gatsby 404 development page acts as handy makeshift.

Go to a non-existent page: http://localhost:8000/xyz

You should now see your WordPress blog posts alongside other pages that Gatsby knows about.

Post Template 1

You may have gathered that our current post template is pretty basic, so clicking on any of our posts will just display the same contents. The important thing to observe is that we now have pages for our WordPress posts and the template is being used for each post.

If you got stuck, you can check out the following Git hash: 13a036ae2a8dea2ea0f7a910c35c2fe4789f9a50

Turning that template into something useful

As you gathered from the last section, all we’ve managed to do with our template is to generate slugs. The means to pull the content is available, but we need to wire it up. Again we’ll only show you the bits you need to add/modify

import { graphql } from 'gatsby'

export default ({ data }) => {
  const post = data.wordpressPost
  return (
    <Layout>
      <div>
        <h1 dangerouslySetInnerHTML={{ __html: post.title  }} />
        <h3>
          date: {post.date} tags: {extractTags(post)}{' '}
        </h3>
        <div dangerouslySetInnerHTML={{ __html: post.content  }} />
      </div>
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode

We can see our function’s signature has changed to allow data to be passed (which will be provided by a GraphQL later on). The key things to notice here is the use of dangerouslySetInnerHTML and the JSX expressions (curly brackets - looks a bit like moustache doesn’t it?).

dangerouslySetInnerHTML is a React’s replacement for innerHTML, why but is it dangerous? Setting HTML from code (or in this case from a database) is risky because it’s easy to expose yourself to XSS). So by using this helper you acknowledging, you’re doing something DANGEROUS, but before we complete freak out, let’s do a quick assessment of the data we’re parsing. Our WordPress blog, with content we’ve written. So unless we have a serious case of self-loathing, it’s unlikely we’re at risk here. If you do need to parse HTML safely, there are options like html-react-parser

If the field our post object we’re interested doesn’t have any markup, then we can use expressions to rendered them {post.date}.

You can stick any valid JavaScript with these expressions and extractTags is a helpful function to flatten the array of tags for our posts. Here’s the definition of that helpful function (add after our default function).

const extractTags = post =>
  post.tags ? post.tags.map(x => x.name).join(', ') : 'none'
Enter fullscreen mode Exit fullscreen mode

Almost there, just need to add our GraphQL that will power this template.

export const pageQuery = graphql`
  query($id: String!) {
    wordpressPost(id: { eq: $id }) {
      title
      tags {
        name
      }
      date(formatString: "Do MMMM YYYY")
      content
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

The pageQuery takes the WordPress Post Id we passed in the context when we created our static pages in gatsby-node.js (see below) and allows us to pull a specific WordPress post using this Id wordpressPost(id: { eq: $id }), we don’t need to pull everything we just need the title, tags, publish date and content. Note: we used GraphQL to format the date.

createPage({
  path: `/${edge.node.slug}`,
  component: path.resolve(`./src/templates/post.js`),
  context: {
    id: edge.node.id,
  },
})
Enter fullscreen mode Exit fullscreen mode

Demo: remember our friend dangerouslySetInnerHTML, you could avoid the risks by using expressions, but as you can see if there’s any markup it will be sanitised i.e. it will be escaped.

Checkpoint: let’s see that content

It’s probably best we restart gatsby to see our changes.

Post Template 2

If you got stuck, you can check out the following Git hash: 50156723a4b21f08baeece9a5f7cd3936e384ee8

To go to the next part of the series, click on the grey dot below which is next to the current marker (the black dot).

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .