Building a Photo Site With GatsbyJS and the WordPress.com API

Share the love

In a recent post, I talked about some recent experimentation I did with GatsbyJS and the WordPress.com API. While it wasn’t quite a fit for rebuilding my personal site, I wanted to build something else with it. So, I elected to build an Instagram-esque photo sharing site. I thought it might be helpful to share a bit of the process so I’ll be walking through the following steps:

We’ll walk through each piece together from start to finish and get a live site working online.

The end result will look like this:

Finished Product.jpg

Part 1 – Getting Setup

First, I’m going to assume you have some tools installed already like Node, npm, and git. If you don’t have them installed already, I personally recommend using Homebrew to install them. Here’s a beginner-friendly post from Treehouse that will get you started.

Once you have the basics installed, we can install the Gatsby CLI tool using the following command:

npm install --global gatsby-cli

The Gatsby CLI tool will allow us to create new sites easily from the command line. Once that has finished, change into the directory where you want to store your code (I use a folder mysteriously titled “Code”) and run the following:

gatsby new photos

This creates a new Gatsby site titled “photos.” If you want to name your project folder something other than “photos” (maybe “photo site”), update the command to whatever you want to use. In my examples, I’ll use “photos-example” so I ran gatsby new photos-example.

Now, let’s change into our directory and install some dependencies. You can do that by running:

cd photos

Replace ‘photos’ with whatever you decided to name your folder.

Installing Some Other Dependencies

There are a few other things we’ll need to install to make this work. Let’s just get them out of the way and install them right now. Inside your app directory, run the following:

npm install --save gatsby-source-wordpress

This installs the basic Gatsby plugin that allows you to source data from WordPress.com. You can read more about it here.

Finally, we need to install some image-related plugins for Gatsby. Install each of the following:

npm install --save gatsby-transformer-sharp

npm install --save gatsby-plugin-sharp

npm install --save gatsby-image

The command line will be your guide if you run into any issues.

Initialize a GitHub Repo

We’re going to be deploying to Netlify from GitHub so you need to setup a Git repository for your project. Go ahead and do that now. If you need help, checkout GitHub’s documentation.

Setup Your Gatsby Config File

The last step is to connect your local site with WordPress.com. To do that, we need to create an app on WordPress.com. Head over to: developer.wordpress.com/apps/. After you login, you’ll want to click “Create a new application.”

For our purposes, we don’t need to worry too much about the information. We won’t be authenticating any other users with our app. It’s just used to give our site access to WordPress.com. Here’s a snapshot of how I set it up:

App.jpeg

After you create your app, you should have a client ID and client secret. The client secret will be a huge string of numbers.

Open up your new app in the text editor of your choice, and find the file titled gatsby-config.js. It should look like this:

Empty Gatsby.jpg

We’re going to add in our Gatsby Source WordPress plugin to pull content from WordPress.com using our app credentials. Here’s a gist of what the entire gatsby-config.js file should look like when you’re done. You can just copy and paste the entire file.


module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
},
plugins: [
{
resolve: 'gatsby-source-wordpress',
options: {
baseUrl: 'YOUR SITE URL',
protocol: 'https',
hostingWPCOM: true,
useACF: false,
auth: {
wpcom_app_clientSecret: 'CLIENT SECRET',
wpcom_app_clientId: 'CLIENT ID',
wpcom_user: 'USERNAME',
wpcom_pass: 'PASSWORD',
},
verboseOutput: false,
},
},
]
}

Some obvious bits you’ll want to replace:

  • “Your Site URL” should be a WordPress.com site URL on your account like jeremeypt.wordpress.com.
  • “Client Secret” and “Client ID” will be found from the app we created above.
  • “Username” and “Password” are your WordPress.com username and password. If you don’t already, please be sure to use a password manager and all the necessary precautions.

Conclusion – Part 1

Once you have saved your gatsby-config.js file, you can head on over to your terminal and run gatsby develop. This will build your site and all necessary pages. If you check your terminal, you should also see that Gatsby is reaching out to WordPress.com and finding some pages and posts on whatever site you connected it to.

It might take a moment, but once it’s finished, it will direct you to http://localhost:8000/ in your browser. You should see the following:

Gatsby Start.jpg

Don’t worry. We’ll remove all of the default content and begin building our app in the next section. This should get us all setup though!

Part 2 – Determining the Layout and Understanding GraphQL Queries

Before we begin building, let’s take a look at a typical Instagram page. It will look something like this:

Instagram.png

A few things stand out:

  • We have a header component that should offer up a quick bio and photo. It should be present on all pages.
  • Instagram has a neat little infinite scroll option. Initially, it only loads 12 photos. When you scroll down though and click on “Load More,” it flips into infinite scroll mode.
  • The photos are laid out in a grid formation three photos across. One thing you’ll notice if you resize your browser or visit Instagram.com on your phone is that the photos are always in a three-across grid formation regardless of screen size. This is a bit different than what I previously expected (a grid that adjusts to one or two across on small screens).

If we were to draw out the anticipated layout then, we would get something like this:

Mockup.png

We’re going to add in a quick footer with some attribution as well at the very end.

Sourcing Data Using GraphQL

The goal with this entire experiment is to use a WordPress.com site as a database of sorts for pulling down data. We want to source as much data as possible from WordPress.com instead of hardcoding it in our app. That way, we can update it directly from WordPress.com without touching a bit of code.

For example, we want the bio to come from the “About Me” section of your profile at wordpress.com/me. We don’t want to code it directly into the app making it difficult to change in the future.

Last time, we set up the Gatsby Source WordPress plugin and added in some configurations that will pull down data from a specific WordPress.com site. We didn’t talk at all about how to actually access that data and use it in our app.

GatsbyJS makes this relatively simple by using a tool called GraphQL. GraphQL is a consistent interface for interacting with your data regardless of the original source. In the particular use case of Gatsby, we can think of it like this:

  • The Gatsby Source WordPress plugin pulls down all posts, pages, comments, etc for your site.
  • Gatsby implements GraphQL to provide a consistent language for interacting with that data so you can pull in what you need for each page.
  • You use GraphQL queries for each page to tell Gatsby the data you need for this particular page to render.

If you’re curious about how Gatsby uses GraphQL, I’d recommend checking out this page.

If you’re new to GraphQL, it can feel a bit foreign at first, but as we walk through building a few pages and components, you’ll get much more familiar with it. Just think of GraphQL as a specific language you can use to interact with and call up various parts of your data whenever you need it.

Let’s see how it might work. Change into the Gatsby directory we built in part 1 of this tutorial and run gatsby develop. That will fire up our site. We can visit http://localhost:8000/ in our browser to see what our site looks like, but we can also test out something else. GraphQL provides an in-browser tool for testing out GraphQL queries. It’s called GraphiQL. Once your site builds, you can test it out by visiting http://localhost:8000/___graphql. You should see this:

GraphQL.png

On the right-hand side, you’ll find a documentation explorer that will come in very handy down the road to find out the specific queries you need. On the left-hand side, you’ll see a query sandbox where you can enter GraphQL queries and see what data is returned. We won’t go into too much more about GraphQL or specific queries in this series, but just know that this playground exists if you ever run into trouble or find yourself wondering about a query.

For now, let’s see how we might return data using GraphQL. On the left-hand side of the screen, remove all of the base text and enter the following:

{
  allWordpressPost {
    edges {
      node {
        title
      }
    }
  }
}

Press the play button at the top to run your query. On the right-hand side, you should see a list of titles from your posts!

GraphQL Example

I’ll provide all the queries we’ll need for this series, but in short, allWordpressPost (capital_P_dangit()) is a built-in query that will look at all posts on the site you have connected. There are others too that we’ll explore like allWordpressWpMe. You’ll notice if you try to type another query in the GraphQL IDE, it will attempt to autocomplete for you as well, which is handy.

Conclusion – Part 2

At this point, we have our designated layout, and we understand a bit more about how we’ll be using our data. The next step is to get building! In the next piece of this series, we’ll figure out how to determine the information we need for our header and build the component to display that information.

Part 3 – Building the Header

Our app is going to be very simple. We’re going to have a handful of components max and only two page templates – the home/grid page and a single photo layout. First, we need to remove some of the unnecessary data that’s present in the initial files leftover from when we first created our new Gatsby site. Your file structure looks a bit like this right know:

Folder Structure.png

Under src/pages/, delete the file titled page-2.js. We’ll work with everything else.

To make everything easy to test and contained, we’re going to create a new folder labeled components that will house the individual components we want to reuse throughout the app. For right now, create that file under src so it should be inline with layouts and pages.

Then, once you have the folder created, go ahead and create a file titled header.js under components. When you’re done, the file structure should look a bit like this:

header.png

To explain some of these files a bit more, anything under the pages folder will be an actual route on our site like /page-2 or something similar. The index.js file under pages serves up our homepage. The 404.js file will (you probably guessed it) serve up a 404 page when we get this up on a server. The layouts folder with index.js holds a generic layout for the homepage.

If you load up the site right now by running gatsby develop in your app directory, you won’t see much difference. Let’s get started on building the Header component though and see what we can come up with.

Building a Header Component

Since we’re mocking up the Instagram layout, we can take a quick peek at the information we’ll need.

Instagram Header.png

We’ll need a username, an avatar, and a bio. That’s simple enough, and thankfully, it’s all available through the WordPress.com API.

In header.js, let’s go ahead and create our new component. We’ll use a pure function. Then, we’ll use the PropTypes declaration to identify the data we’ll need. Finally, we’ll return some HTML to see if this thing is actually on. You could have something like this:


import React from 'react'
import proptypes from 'prop-types'
const Header = () => {
return (
<h1>Sup</h1>
)
}
Header.PropTypes = {
src: proptypes.string,
username: proptypes.string,
bio: proptypes.string,
}
export default Header;

view raw

header.js

hosted with ❤ by GitHub

It’s not doing much, but at least our Header component should be returning an h1 now with some text. Let’s test it out. Save this file then head over to src/components/index.js. Leave the parent div in place under the IndexPage but remove the extra default HTML added by Gatsby. Finally, import your new Header component and render it to the page. Your src/components/index.js file could look a bit like this:


// External
import React from 'react';
import Link from 'gatsby-link';
// Internal
import Header from '../components/header';
const IndexPage = () => (
<div>
<Header />
</div>
)
export default IndexPage;

view raw

index.js

hosted with ❤ by GitHub

Of note, you may be wondering why we had to use a return statement in our Header component, but you don’t see one in IndexPage. Pro-tip: The usage of const MyComponent = () => () implies a return statement whereas const MyComponent = () => {} does not. I prefer the latter with an added return just to make the return extra clear.

If you save the file once you’ve added the Header component and refresh your page (it should refresh automatically on save), you’ll get something like this:

Gatsby Header.png

It’s not much, but at least we have something rendering to the page now!

Okay, let’s go back to our Header component and flesh it out a bit more. As you probably guessed, I’ve already worked through the code required to make this header look like the header from Instagram. I’ll share some finished code along the way, but I would encourage you to experiment if you would like. Some things to keep in mind:

  • GatsbyJS uses CSS-in-JS by default, which if you haven’t seen it before, can look a bit strange. When in doubt, camelcase your property and include quotes around your values. For example, padding-bottom: 10px; becomes paddingBottom: '10px',. You’ll get the hang of it.
  • We want our `Header` component to be dumb and predictable. Meaning, we don’t want it to fetch data of any kind. We want to pass in data from a higher component. We’ll sketch out the `Header` component with some dummy data then find out how to replace it with actual data in a second.

Here’s the Header component a bit more fleshed out with dummy data. It looks really long, but that’s mostly due to how I like to style my JavaScript. It’s basically a parent div with an image, header, and some paragraph text. The username uses Gatsby’s Link component to link back to the homepage so we have a way of navigating around.


import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
const Header = () => {
const bio = 'Lorem Ipsum is simply dummy text that I want to appear on the screen.';
const username = 'username';
const src = 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=retro';
return (
<div
style={ {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
margin: '2em 2em',
} }
>
<span
style={ {
flexBasis: '120px',
height: '96px',
} }
>
<img
src={ src }
alt={ `Jeremey DuVall` }
style={ {
marginBottom: 0,
borderRadius: "50%",
width: '96px',
} }
/>
</span>
<span
style={ {
flexBasis: '500px',
flexGrow: 1,
} }
>
<Link
to='/'
activeStyle={ {
textDecoration: 'none',
color: '#000000'
} }
>
<h3
dangerouslySetInnerHTML={ { __html: ( username ) } }
style={ { marginBottom: '0.2em' } }
/>
</Link>
<p
style={ { marginBottom: 0 } }
dangerouslySetInnerHTML={ { __html: ( bio ) } }
/>
</span>
</div>
)
}
Header.PropTypes = {
src: PropTypes.string,
username: PropTypes.string,
bio: PropTypes.string,
}
export default Header;

view raw

header.js

hosted with ❤ by GitHub

I will say that Flexbox makes this entire thing much easier so if you haven’t already, be sure to check out a tutorial on Flexbox somewhere online.

Save that Header file. If you flip over to your browser, you should see your header getting rendered to the page, which is awesome! However, you’re also going to see the default Gatsby header still. Let’s fix that.

Head over to src/layouts/index.js. You’ll see a component called TemplateWrapper and another header component that Gatsby includes by default. We can remove all of the default text. All that we need there is the following for right now:


import React from 'react';
import PropTypes from 'prop-types';
import './index.css';
const TemplateWrapper = ( { children } ) => (
<div>
<div
style={ {
margin: '0 auto',
maxWidth: 960,
padding: '0px 1.0875rem 1.45rem',
paddingTop: 0,
} }
>
{ children() }
</div>
</div>
)
TemplateWrapper.propTypes = {
children: PropTypes.func,
}
export default TemplateWrapper

view raw

index.js

hosted with ❤ by GitHub

Voila! You should now see something like this with a dummy Gravatar and our Lorem Ipsum text:

dummy data.png

Import the Data From WordPress.com

Hard coding the data is fun, but it’s not practical. It would be really nice to pull the data directly from WordPress.com so we don’t have to open up the text editor any time we want to edit our bio.

As explained above, we want our Header component to be dumb meaning we don’t want it to fetch data. Instead, it should just use the data we pass down to it.

We’ll use a GraphQL query in the IndexPage to grab some data we’ve pulled down from WordPress.com. Again, if you haven’t used GraphQL before, it can look a bit different. If you need to test out any of the queries, I recommend starting at http://localhost:8000/___graphql, which is the link to your GraphQL IDE when your site is up and running. Here’s the query we’ll use:

export const pageQuery = graphql`
    query IndexQuery {
        allWordpressWpMe {
            edges {
                node {
                    name
                    description
                    avatar_urls {
                        wordpress_96
                    }
                }
            }
        }
    }
`

This reaches out to allWordpressWpMe, which is exposed as part of GatsbyJS. It grabs the name, description, and avatar source URL from the account you connected way back in part 1.

We’ll use it like this at the end of the file. This is what your src/pages/index.js page should look like at this point:


// External
import React from 'react';
import Link from 'gatsby-link';
// Internal
import Header from '../components/header';
const IndexPage = ( props ) => {
return (
<div>
<Header />
</div>
)
}
export default IndexPage
export const pageQuery = graphql`
query IndexQuery {
allWordpressWpMe {
edges {
node {
name
description
avatar_urls {
wordpress_96
}
}
}
}
}
`

view raw

index.js

hosted with ❤ by GitHub

You’ll notice I modified the main component a bit to include an explicit return statement.

Alright – we’re almost there! Now, if you drop a console.log( props ) before the return statement, you’ll find that we now have a data.allWordpressWpMe object available. If you inspect it a bit more, you’ll find that it has all of the information we need. We just need to pass it down to the Header component and tell the component to use the passed down data, not the dummy data. Here’s what both files will look like when you’re done.

src/pages/index.js


// External
import React from 'react';
import Link from 'gatsby-link';
// Internal
import Header from '../components/header';
const IndexPage = ( props ) => {
const bio = props.data.allWordpressWpMe.edges[0].node.description;
const username = props.data.allWordpressWpMe.edges[0].node.name;
const avatar = props.data.allWordpressWpMe.edges[0].node.avatar_urls.wordpress_96;
return (
<div>
<Header bio={ bio } username={ username } src={ avatar } />
</div>
)
}
export default IndexPage
export const pageQuery = graphql`
query IndexQuery {
allWordpressWpMe {
edges {
node {
name
description
avatar_urls {
wordpress_96
}
}
}
}
}
`

view raw

index.js

hosted with ❤ by GitHub

src/components/header.js


import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
const Header = ( props ) => {
const bio = props.bio;
const username = props.username;
const src = props.src;
return (
<div
style={ {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
margin: '2em 2em',
} }
>
<span
style={ {
flexBasis: '120px',
height: '96px',
} }
>
<img
src={ src }
alt={ `Jeremey DuVall` }
style={ {
marginBottom: 0,
borderRadius: "50%",
width: '96px',
} }
/>
</span>
<span
style={ {
flexBasis: '500px',
flexGrow: 1,
} }
>
<Link
to='/'
activeStyle={ {
textDecoration: 'none',
color: '#000000'
} }
>
<h3
dangerouslySetInnerHTML={ { __html: ( username ) } }
style={ { marginBottom: '0.2em' } }
/>
</Link>
<p
style={ { marginBottom: 0 } }
dangerouslySetInnerHTML={ { __html: ( bio ) } }
/>
</span>
</div>
)
}
Header.PropTypes = {
src: PropTypes.string,
username: PropTypes.string,
bio: PropTypes.string,
}
export default Header;

view raw

header.js

hosted with ❤ by GitHub

Conclusion – Part 3

At long last, we should have our header in place with data pulled programmatically from WordPress.com!

Final Header.png

Part 4 – Building a Grid Photo Layout

In this fourth part, we’ll put together the grid layout for the homepage. As we did last time with the Header component, we obviously want to pull our photos from WordPress.com; that’s the entire purpose of building this fun little tool.

To do that, we’ll put together a GraphQL query that returns a list of posts on your site and their featured images. That’s how we’ll display the image. It will be the featured image of a specific post. If you don’t already have some posts on your site with featured images, go ahead and update that through WordPress.com.

Once you have done that, let’s head over to the GraphiQL IDE (http://localhost:8000/___graphql). Enter the following query:

allWordpressPost( sort: { fields: [ date ], order: DESC } )
    edges {
        node {
            id
            title
            slug
            date( formatString: "/YYYY/MM/DD/" )
            featured_media {
                localFile {
                    childImageSharp {
                        id
                    } 
                } 
            } 
        } 
    }
}

This looks at all the WordPress posts returned from your site. It filters them by date in a descending order (so the most recent is at the top). Then, for each node it finds, it returns the id, title, slug, date in a specified format, and featured media file. This highlights a few fun facts about Gatsby/GraphQL:

  • With GraphQL, you can tell it the format you want the date returned in.
  • Gatsby automatically imports and optimizes the featured image for you, which is why we can use `localFile` on the `featured_media` property. We’ll see more of how this works in a second.

If you run the query, you should get something that looks like this:

GraphQL Post Query.png

Now, let’s combine that with our other GraphQL query we used for the Header component on src/pages/index.js. The resulting query should look like this:


export const pageQuery = graphql`
query IndexQuery {
allWordpressWpMe {
edges {
node {
name
description
avatar_urls {
wordpress_96
}
}
}
}
allWordpressPost( sort: { fields: [ date ], order: DESC } ) {
edges {
node {
id
title
slug
date( formatString: "/YYYY/MM/DD/" )
featured_media {
localFile {
childImageSharp {
sizes( maxWidth: 1000 ) {
…GatsbyImageSharpSizes
}
}
}
}
}
}
}
}
`

view raw

index.js

hosted with ❤ by GitHub

The only major difference between this query and the one we’re using on the page is the use of childImageSharp and the fragment ...GatsbyImageSharpSizes. This has to do with how Gatsby processes images. If you’re curious to learn more, I would encourage you to read this page.

Now, if we save this file and console.log( props ), we’ll find that we have data.allWordpressPost available to use!

Building the Photo and Photo Row Components

As I mentioned previously, the Instagram layout isn’t what I would have expected on small screens. On a typical desktop, Instagram has three photos to a row. When you shrink down to a small screen though, it still has three photos in a row instead of reorganizing to feature on larger photo at a time.

Mobile Instagram

To recreate that layout, we’re going to create two “dumb” components. One will be an individual photo. The other will be a photo row that will hold three photos across.

Within our components folder, create two new files – row.js and photo.js. Here are some full gists with the code that should go inside:

Photo.js


// External
import React from 'react';
import PropTypes from 'prop-types';
import Img from 'gatsby-image';
import Link from 'gatsby-link';
const Photo = ( props ) => {
return (
<div
style={ {
width: 'calc( 100% / 3 )',
padding: '0.2em',
} }
>
<Link to={ props.link } >
<Img
src={ props.src }
sizes={ props.sizes }
style={ {
width: 'auto',
height: 'auto',
} }
/>
</Link>
</div>
)
}
Photo.PropTypes = {
src: PropTypes.string,
sizes: PropTypes.object,
link: PropTypes.string,
}
export default Photo;

view raw

photo.js

hosted with ❤ by GitHub

Row.js


// External
import React from 'react';
import PropTypes from 'prop-types';
// Internal
import Photo from './photo';
const PhotoRow = ( props ) => {
return (
<div
style={ {
display: 'flex',
flexDirection: 'row',
width: '100%',
} }
>
{
props.photos.map( photo => {
return (
<Photo
src={ photo.node.featured_media.localFile.childImageSharp.sizes.src }
sizes={ photo.node.featured_media.localFile.childImageSharp.sizes }
key={ photo.node.id }
link={ photo.node.date + photo.node.slug }
/>
)
} )
}
</div>
)
}
PhotoRow.PropTypes = {
photos: PropTypes.array,
}
export default PhotoRow;

view raw

row.js

hosted with ❤ by GitHub

You’ll notice a few things:

  • Neither component is querying data. They just expect certain things.
  • row.js expects an array of photos to be provided. It then uses the Photo component to return something.
  • Photo.js expects a src string, sizes object, and link string. It then uses the built-in Gatsby Img component along with the Link component we used previously.

If you refresh the page at this time, you won’t see much. Let’s head back over to our src/pages/index.js file to bring this all together.

Outputting Photos on the Homepage

Import the Row component into src/pages/index.js. At this point, we have an array of posts provided by allWordpressPost. We also have a Row component that is designed to output the photo. What we need to do is loop over the posts array and return every three items. If we’re at the end of the array and we have a straggler (one or two photos left over), those should be returned in their own array.

We can do that with the following two functions:


const photos = props.data.allWordpressPost.edges;
const displayPhotos = () => {
const photoArray = [];
let photoRow = [];
let count = 0;
photos.map( photo => {
if ( photo.node.featured_media ) {
photoArray.push( photo );
}
} );
return (
photoArray.map( photo => {
if ( photoRow.length === 3 ) {
photoRow = [];
}
photoRow.push( photo );
count++;
if ( photoRow.length === 3 ) {
return returnRow( photoRow, count );
} else if ( photoArray.length count === 0 ) {
return returnRow( photoRow, count );
}
} )
)
}
const returnRow = ( photos, count ) => {
return (
<PhotoRow photos={ photos } key={ count } />
)
}

The first function displayPhotos() is the primary function we’ll call somewhere in our code to get some photos. It loops over the posts (which is assigned to a variable called photos at the very top). Then, if a featured_media property exists, it pushes the post onto an array called photoArray. This check is important because we want to make sure the photo actually has a featured image we can use.

Then, the function maps over the new photoArray and keeps a count. Whenever the count reaches three, it returns a photoRow with the photos and resets the count. At the end, it returns the remainder if photoArray.length - count === 0.

Finally, call the displayPhotos() function in our return statement like this:


return (
<div>
<Header bio={ bio } username={ username } src={ avatar } />
{ displayPhotos() }
</div>
)

view raw

index.js

hosted with ❤ by GitHub

Now, save and refresh the page. You should end up with a grid layout like this:

Grid.png

If you scale down to small screens, you’ll notice that it adjusts just like the Instagram layout staying in a three-photo row the entire time.

Grid Mobile.png

Conclusion – Part 4

Right now, if you click on a photo, you’ll notice it takes you to our 404 page. In the next part, we’ll build the individual photo pages so the links work correctly.

Part 5 – Building the Individual Photo Page

Right now, our front page grid is looking great, but if you click on an individual photo, nothing happens. We need to do two things to fix this.

First, we need to create a template for individual photo pages. That way, GatsbyJS will know what content to display on individual routes. Second, we need to edit the gatsby-node.js file to actually create the routes for the individual posts. We’re going to tackle them in reverse order building the routes first.

Building Individual Routes

GatsbyJS has a built-in createPage function that you can call to build individual pages (like /example-post) whenever you run gatsby develop or gatsby build. When you run those commands, Gatsby looks through the gatsby-node.jsfile and builds the necessary HTML for any pages on your site. That way, we can render those individual pages. Let’s walk through how we would edit our gatsby-node.js file for building an individual post page.

For right now, we’re going to build a generic template to make sure this is all working correctly. Create a new folder called ‘templates’ that will live at src/templates. In that new folder, create a single file titled single-photo.js. The templates folder will represent just that – templates we’ll use to build individual pages on our site. Within single-photo.js drop this code:


// External
import React from 'react';
import PropTypes from 'prop-types';
// Internal
import Header from '../components/header';
const SinglePhoto = ( props ) => {
const bio = props.data.allWordpressWpMe.edges[0].node.description;
const username = props.data.allWordpressWpMe.edges[0].node.name;
const avatar = props.data.allWordpressWpMe.edges[0].node.avatar_urls.wordpress_96;
return (
<div>
<Header bio={ bio } src={ avatar } username={ username } />
<div>hi</div>
</div>
)
}
export default SinglePhoto;
SinglePhoto.PropTypes = {
data: PropTypes.object
}
export const pageQuery = graphql`
query PhotoQuery {
allWordpressWpMe {
edges {
node {
name
description
avatar_urls {
wordpress_96
}
}
}
}
}
`

view raw

single-photo.js

hosted with ❤ by GitHub

This is a generic layout that will get us started. It just renders our header to the page with some data queried from GraphQL. Then, it gives us a div with “hi” inside so we can make sure we’re rendering something to the page.

Now, let’s head on over to the gatsby-node.js file, which should be in the root directory for your app. Right now, it should be blank, but we’re going to drop in this code:


const _ = require(`lodash`)
const Promise = require(`bluebird`)
const path = require(`path`)
const slash = require(`slash`)
exports.createPages = ( { graphql, boundActionCreators } ) => {
const { createPage } = boundActionCreators;
return new Promise( ( resolve, reject ) => {
graphql(
`
{
allWordpressPost {
edges {
node {
date( formatString: "YYYY/MM/DD/" )
slug
}
}
}
}
`
).then( result => {
if (result.errors) {
console.log(result.errors)
reject(result.errors)
}
const template = path.resolve(`./src/templates/single-photo.js`);
_.each( result.data.allWordpressPost.edges, edge => {
createPage( {
path: edge.node.date + edge.node.slug,
component: slash( template ),
context: {
slug: edge.node.slug
},
} )
} )
} )
resolve();
} )
}

view raw

gatsby-node.js

hosted with ❤ by GitHub

Alright – let’s walk through what this code is doing step-by-step:

  • First, it’s creating a promise. In JavaScript, a promise is like a contract. We’re going to wait until an operation finishes and then do something with the results. In this case, we’re going to use allWordpressPost to query the individual posts from our site including the date and slug for each post.
  • Next, if it hits some errors, we’re going to log those out.
  • Otherwise, if it doesn’t hit any errors, it looks at the path to our new single photo template and stores that in a variable called template. Then, it uses the built-in createPages function to generate a new page using that template. It creates a path of date + slug (i.e. 2017/12/11/this-post) to match the default WordPress.com permalink structure. Finally, it gives the createPage function a bit of context. We’ll find out why that’s important here in a bit.

Now, stop your app and restart it with the gatsby develop command. When it builds, try visiting a route that you know doesn’t exist like http://localhost:8000/this-cant-be-a-route. You’ll hit the 404 page that’s automatically generated. More importantly though, you’ll see a list of all pages on your site. If everything goes according to plan, you should see all of the pages for our posts like this!

Even better, if you click on a page, you should see our header with “hi” printed to the page. It works!

Share the love
Strategies on solving problems and wowing customers every Sunday 👉
Strategies for solving problems and wowing customers 👇