2019-08-04
|~8 min read
|1500 words
Alright, I’ve been building to this point! Time to generate new pages for a blog programmatically using Gatsby!
Previously I wrote about configuring Gatsby’s gatsby-source-filesystem plugin1 and writing GraphQL queries that take variables.2 This post will combine these two lessons to generate pages to follow a simple layout for each post in my filesystem.
This post begins assuming a configured gatsby-source-filesystem and at the point where I’m ready to set up my gatsby-node file to create pages.
First, the gatsby-node is one of several files that Gatsby looks for in the root directory (gatsby-config is another as an example).
Gatsby comes with a suite of built-in plugins. To make use of any of them, you export them from the gatsby-config file. I’ll be using the createPages asynchronous operation.
For example:
exports.createPages = async ({ actions, graphql, reporter }) => {
// In node (which this is), graphql is a function that takes a string.
const result = await graphql(`
query {
allMdx {
nodes {
frontmatter {
slug
}
}
}
}
`)
if (result.errors) {
reporter.panic("failed to create posts", result.errors)
}
const posts = result.data.allMdx.nodes
posts.forEach((post) => {
const path = post.frontmatter.slug
actions.createPage({
path,
component: require.resolve("./src/templates/post.js"),
context: {
slug: post.frontmatter.slug,
},
})
})
}Walking through this function:
result to the returned value of the GraphQL query. The returned result is the slug. Notice that in node the GraphQL query is wrapped in ().result returns a promise with a .error attribute 3)
reporter is a built-in console log. Panic is an error state.
forEach to generate a page using Gatsby’s built-in actions. Gatsby uses Redux to manage state and ships with a series of built in actions (of which createPage is one). 4>The createPage action API takes an object, and two optional parameters (plugin and actionOptions ) neither of which I need for this simple use case.
The object I identify has three things:
At this point, I’m ready to look at the post file I’ve noted as the component to in the action.
For example - a simple Post layout could look like:
import React from ‘react’;
import { graphql, Link } from ‘gatsby’;
import { MDXRenderer } from ‘gatsby-mdx’;
import Layout from ‘../components/layout’;
export const query = graphql`
query($slug: String!) {
mdx(frontmatter: { slug: { eq: $slug } }) {
frontmatter {
title
author
}
code {
body
}
}
}
`;
const PostTemplate = ({ data: { mdx: post } }) => {
const { frontmatter, code } = post;
return (
<Layout>
<h1>{frontmatter.title}</h1>
<p style={{fontSize: '0.75rem'}}>
{`posted by ${frontmatter.author}`}
</p>
<MDXRenderer>{code.body}</MDXRenderer>
<Link to="/">Return to home</Link>
</Layout>
);
};
export default PostTemplate;A few notes here:
The slug that’s being passed in from the gatsby-node configuration is the context with which I’m going to query for the data to generate the post.
I wrote my blog posts as .mdx files and resolve any .mdx or .md files using the gatsby-mdx plugin. The nice thing about doing this is that I can use the MDXRenderer to easily render the body of the posts.
What does all of this mean?
Any time I add a new post to my filesystem that is returned in my all MDX query in the node modules, it will get passed through to the a createPage action and rendered with the MDXRenderer in a basic format of:
Pretty nifty!
Alright, I’ve been building to this point! Time to generate new pages for a blog programmatically using Gatsby!
Previously I wrote about configuring Gatsby’s gatsby-source-filesystem plugin1 and writing GraphQL queries that take variables.2 This post will combine these two lessons to generate pages to follow a simple layout for each post in my filesystem.
This post begins assuming a configured gatsby-source-filesystem and at the point where I’m ready to set up my gatsby-node file to create pages.
First, the gatsby-node is one of several files that Gatsby looks for in the root directory (gatsby-config is another as an example).
Gatsby comes with a suite of built-in plugins. To make use of any of them, you export them from the gatsby-config file. I’ll be using the createPages asynchronous operation.
For example:
exports.createPages = async ({ actions, graphql, reporter }) => {
// In node (which this is), graphql is a function that takes a string.
const result = await graphql(`
query {
allMdx {
nodes {
frontmatter {
slug
}
}
}
}
`)
if (result.errors) {
reporter.panic("failed to create posts", result.errors)
}
const posts = result.data.allMdx.nodes
posts.forEach((post) => {
const path = post.frontmatter.slug
actions.createPage({
path,
component: require.resolve("./src/templates/post.js"),
context: {
slug: post.frontmatter.slug,
},
})
})
}Walking through this function:
result to the returned value of the GraphQL query. The returned result is the slug. Notice that in node the GraphQL query is wrapped in ().result returns a promise with a .error attribute 3)
reporter is a built-in console log. Panic is an error state.
forEach to generate a page using Gatsby’s built-in actions. Gatsby uses Redux to manage state and ships with a series of built in actions (of which createPage is one). 4>The createPage action API takes an object, and two optional parameters (plugin and actionOptions ) neither of which I need for this simple use case.
The object I identify has three things:
At this point, I’m ready to look at the post file I’ve noted as the component to in the action.
For example - a simple Post layout could look like:
import React from ‘react’;
import { graphql, Link } from ‘gatsby’;
import { MDXRenderer } from ‘gatsby-mdx’;
import Layout from ‘../components/layout’;
export const query = graphql`
query($slug: String!) {
mdx(frontmatter: { slug: { eq: $slug } }) {
frontmatter {
title
author
}
code {
body
}
}
}
`;
const PostTemplate = ({ data: { mdx: post } }) => {
const { frontmatter, code } = post;
return (
<Layout>
<h1>{frontmatter.title}</h1>
<p style={{fontSize: '0.75rem'}}>
{`posted by ${frontmatter.author}`}
</p>
<MDXRenderer>{code.body}</MDXRenderer>
<Link to="/">Return to home</Link>
</Layout>
);
};
export default PostTemplate;A few notes here:
The slug that’s being passed in from the gatsby-node configuration is the context with which I’m going to query for the data to generate the post.
I wrote my blog posts as .mdx files and resolve any .mdx or .md files using the gatsby-mdx plugin. The nice thing about doing this is that I can use the MDXRenderer to easily render the body of the posts.
What does all of this mean?
Any time I add a new post to my filesystem that is returned in my all MDX query in the node modules, it will get passed through to the a createPage action and rendered with the MDXRenderer in a basic format of:
Pretty nifty!
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!