Seperating posts and pages in Gatsby


Updated for V2

26/6/21: I haven't used gatsby in a while, no idea if this still works

I started with the blog starter which includes the nessisary plugin for working with markdown. If you need to add markdown see this page:

As of V2, the blog content is in content/blog. We'll be adding markdown pages into content/pages. The idea is that you can have a page in src/pages/page.js or as a markdown page in src/pages/

Step 1 - Source files from our new folder

The location of the sourced blog posts is in gatsby-config.js. All we need to do is copy the blog post section and change to our pages location.

Like so:

resolve: `gatsby-source-filesystem`,
options: {
name: `page`,
path: `${__dirname}/src/pages/`,
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/posts`,
name: 'post',

note that I've changed blog to posts - just a naming preference.

You need to restart Gatsby for these changes to take effect.

Now all of the files in the pages folder will be fetched.

Step 2 - filter pages out of blog roll

Now here’s the really important bit. All of these will come in to the same source. That’s fine if you want all your pages in your blog roll, but you probably don't.

So we need to filter out the pages in the graphql query.

In the blog roll page - src/pages/index.js in the blog starter template, the graphql query is bringing in all markdown files sourced.

Under the sort option we will add a filter to

sort: { fields: [frontmatter___date], order: DESC }
filter: { sourceInstanceName: { eq: "post" }

You could also do this via sourceInstanceName but you'd need to change allMarkdownRemark to allFile, this way is simplier to me.

Tip: To test your query use http://localhost:8000/___graphql

Seperate templates

I wanted to have a seperate templates for my post and pages. So I needed a way to do this. As I see it, there are two ways.

First would be to use GraphQL twice to create a filter for posts and another request for pages. But that seemed like a lot of code duplication so I went for option two which was to use a frontmatter - “layout: page”. How you would select a layout in Jekyll.

So on my pages I added that to the front matter at the top.

title: About
layout: page

Then modified gatsby-node.js, to choose the template based on the choosen layout.

Near the top, add a new constant for the page template:

const postTemplate = path.resolve('./src/templates/blog-post.js')
const pageTemplate = path.resolve('./src/templates/page.js')

In the posts for each loop we add a if statement to select the page template if it's set, just before the createPage call. The component option in createPage should be changed to the template var.

 posts.forEach((post, index) => {
const previous = index === posts.length - 1 ? null : posts[index + 1].node
const next = index === 0 ? null : posts[index - 1].node

// Choose the blog or the page template
template = blogPost
if (post.node.frontmatter.layout === "page") {
var template = pageTemplate

path: post.node.fields.slug,
component: template,
context: {
slug: post.node.fields.slug,

You need to add the layout option to the GraphQL query to to make it available, add it under the frontmatter heading as so:

allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }, limit: 1000) {
edges {
node {
fields {
frontmatter {

This is a guist for v1, yet to be updated to V2, but still gives you an idea.

There is some info in the discusion about this problem here