Building a Blog with Gatsby - 3
Posted 10 December, 2019
Welcome to the third section of the Building a blog with Gatsby Series.
In the last section, we grabbed frontmatter data from our Blog Posts using GraphQL.
In this section, we are going to create post previews of each blog posts and display them on the home page. We are also going to create pages for each blog post.
Let's get started!!
Creating a post preview component
We are going to create a very simple post preview component so create a file in your components folder called PostPreview.jsx.
import React from "react";
const postPreviewStyle = {
border: "1px solid #afafaf",
padding: "1.5rem 0.5rem",
borderRadius: "5px",
margin: "1rem 0",
}
const PostPreview = ({title, author,topic}) => {
return(
<div style = {postPreviewStyle}>
<h1>First Blog Post</h1>
<p>By Faruq</p>
<p>Topic: Gatsby</p>
</div>
)
}
export default PostPreview;That's our very simple PostPreview component. We will replace the placeholder text with real data from our blog posts. We will get this data from the props object of our component which was destructured into title, author and topic. I used jsx inline styling, you can use emotion or any other preferred method of styling. If you are using emotion, there's a gatsby plugin for emotion so you should check that out.
Using Frontmatter data
Now we are going to use the data we queried for in the last section.
We are going to import useStaticQuery and graphql from gatsby into our index.js page.
import { graphql, useStaticQuery } from "gatsby";Now to grab the data from the query, we will use the useStaticQuery hook inside the IndexPage component.
const data = useStaticQuery(graphql`
query MyQuery {
allMdx {
nodes {
frontmatter {
Author
Topic
title
}
}
}
}
`);Now we can access the array of frontmatter data using data.allMdx.nodes. We can then map over the array and return a post preview for each blog post.
import React from "react"
import { Link } from "gatsby"
import PostPreview from "../components/PostPreview";
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
import { graphql, useStaticQuery } from "gatsby";
const IndexPage = () => {
const data = useStaticQuery(graphql`
query MyQuery {
allMdx {
nodes {
frontmatter {
Author
Topic
title
}
}
}
}
`);
const posts = data.allMdx.nodes;
return (
<Layout>
<SEO title="Home" />
<h1>Welcome to my Blog</h1>
<p>I welcome you</p>
<p>Welcome once again! <span role="img">😉</span></p>
<Link to="/page-2">Go to page 2</Link>
{posts.map(
(post) => <PostPreview
title = {post.frontmatter.title}
author = {post.frontmatter.Author}
topic = {post.frontmatter.Topic}/>
)}
</Layout>
)
}
export default IndexPageNow, that is the code for our Index page. As you can see, we stored the array of blog posts in a variable called posts. We then mapped over the posts array and returned a PostPreview component for each post. We also set the props to their appropriate values.
Now we will edit the PostPreview component to display the actual data in the props.
const PostPreview = ({ title, author, topic }) => {
return (
<div style={postPreviewStyle}>
<h1>{title}</h1>
<p>{author}</p>
<p>Topic: {topic}</p>
</div>
)
}Now when you run npm run develop, you should see post previews of all 3 blog posts.
Generating Pages for each Blog Post
Now we are going to generate pages for each blog post.
To do that we will go to our gatsby-node.js file. If you don't have one, you can create an empty gatsby-node.js file in the root directory.
For this, we use gatsby's createPages API to create our pages.
exports.createPages = async ({actions, graphql, reporter}) => {
const result = await graphql(`
query MyQuery {
allMdx {
nodes {
frontmatter {
title
}
}
}
}
`);
if(result.errors){
reporter.panic("failed to create posts", results.errors);
}
const blogPosts = result.data.allMdx.nodes;
blogPosts.forEach(post => {
actions.createPage({
path: post.frontmatter.title,
component: require.resolve("./src/templates/post.jsx"),
context: {
title: post.frontmatter.title
}
})
});
}As you can see, in our async function we grabbed the title of all our MDX files from the data layer. The await keyword basically means "Let's wait for the data we queried" and once that data is ready, we store it in the variable results.
We also checked for errors after which we grabbed the nodes (array of the data we need) and assigned it to the variable blogPosts.
Here comes the interesting part!
For each title we grabbed, we created a page and let the path to that page be the title. We also gave each page a template component which we will create soon. This template component which we called post.jsx dictates how our pages will look like. After, we gave each page a context. Context refers to whatever data you want to pass to each page. We put the title of each page in our context and this allows us to uniquely identify each blog post since all posts have different titles. Ideally, you would use a slug(unique identifier) here.
So a GraphQL variable called title will be passed to each page.
Now we will create our post component.
Creating post.jsx
For post.jsx, we can start with a simple functional component.
import React from "react"
import Layout from "../components/layout"
const PostTemplate = () => {
return(
<Layout>
</Layout>
)
}
export default PostTemplate;Now we will use our GraphQL variable, title. As I said before, each page will have a GraphQL variable called title.
We will query some data with GraphQL and that data will become available to our PostTemplate component as part of its props.
Let's get started!
export const data = graphql`
query ($title: String!) {
mdx(frontmatter: {title: {eq: $title}}) {
frontmatter {
title
Author
Topic
}
body
}
}
`;In the above query, we grabbed the mdx file whose title was equal to the GraphQL variable, title, that was passed to the page. We then grabbed the title, Author and Topic of that mdx file. We also grabbed the body of the mdx file which we will render using MDXRenderer from gatsby-plugin-mdx.
Now we can access data using props.data inside our component. Now we will add some content in between our Header and Footer(which were provided by the layout component).
const PostTemplate = ({ data: { mdx } }) => {
return (
<Layout>
<Link to = "/">← Go Home</Link>
<h1>{mdx.frontmatter.title}</h1>
<p>{`Written by ${mdx.frontmatter.Author}`}</p>
<p>{mdx.frontmatter.Topic}</p>
<MDXRenderer>
{mdx.body}
</MDXRenderer>
</Layout>
);
}Instead of using props and having to write props.data.mdx.frontmatter.title to get our page title, I destructured props so now we just have to write mdx.frontmatter.title.
Note: You should import the following at the top of the post.jsx file
import React from "react";
import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import {Link} from "gatsby";
import Layout from "../components/layout";Now we are almost done, all we need is a link to access the contents of each blog post. In your PostPreview.jsx component, make the necessary changes as shown below
const PostPreview = ({ title, author, topic }) => {
return (
<div style={postPreviewStyle}>
<h1>{title}</h1>
<p>{author}</p>
<p>Topic: {topic}</p>
<Link to = {`/${title}`}>Read Post</Link> //add this line
</div>
)
}Since the path to each page is the title, I simply added a link which led to that path.
Now our blog is complete!!
You have made this far!
If you have any questions, please feel free to reach out to me on twitter.
You can find the repo of this series here.
You can find the finished blog here.