Crafting Effective Related Posts for Your Gatsby Blog
Add to your RSS feed17 October 20247 min readTable of Contents
We’ll be using Gatsby, TailwindCSS, and Shadcn UI for this tutorial. If you’re unsure how to set up a project with these tools, please check out our previous posts in the Gatsby category. In this guide, we won't be building a blog from scratch or filling it with random content. Instead, we’ll assume you already have a blog with existing content organized by categories, and you're looking to display related posts.
Related posts are an excellent way to increase reader engagement on your blog. They help keep visitors on your site longer by offering them content that aligns with their interests. This article will guide you through creating effective related posts for your Gatsby blog, covering best practices, tips, and examples to enhance user experience.
Let’s start by creating the Related Posts component.
Why Related Posts Matter
Related posts enhance user experience by offering readers additional content they are likely to find valuable. Some key benefits include:
- Increased engagement: Related posts encourage readers to explore more of your content, reducing bounce rates.
- Improved SEO: By linking relevant articles together, you create a network of internal links, which can help with SEO.
- Higher page views: Visitors are more likely to stay on your site if you guide them toward content that aligns with their interests.
- Better user experience: Offering more relevant articles improves navigation and keeps users engaged with high-quality content.
Now, let’s explore how you can create effective related posts in your Gatsby blog.
Related Posts Component
The design of your related posts section should be clean and visually appealing. It should fit seamlessly within your post layout but still stand out to draw attention. Consider using images, headlines, and brief descriptions.
1. Create a related-posts folder inside a components dir**.
2. Create a related-card.types.tsx file:
1 // related-card.types.tsx23 export type RelatedPost = {4 frontmatter: {5 desc: string;6 permalink: string;7 title: string;8 }9 id: string;10 }
3. Create a related-card.tsx components:
We’ll use the card component from Shadcn. First, instantiate the component:
1 // related-card.tsx23 import React from 'react';4 import { Link } from 'gatsby';56 import { Card, CardContent, CardTitle } from '../../ui/card';7 import { RelatedPost } from './related-card.types';89 type RelatedCardPostProps = {10 post: RelatedPost;11 };1213 const RelatedCard = ({ post }: RelatedCardPostProps) => {1415 return (16 <Card className='md:flex justify-start p-3 max-h-fit max-w-[45%] md:items-center hover:shadow-black dark:hover:shadow-white'>17 <CardContent className='p-0'>18 <Link to={`/${post.frontmatter.permalink}`}>19 <CardTitle className='inline-block font-medium text-lg mt-1'>20 {post.frontmatter.title}21 </CardTitle>22 </Link>23 </CardContent>24 </Card>25 );26 };27 export default RelatedCard;
Now let’s bring it all together into a wrapper, which we’ll call related-posts.tsx
1 import React from 'react';23 import { RelatedPost } from './cards/related-card.types';4 import RelatedCard from './cards/related-card';56 type RelatedPostsProps = {7 posts: RelatedPost[];8 };910 const RelatedPosts = ({ posts }: RelatedPostsProps) => {11 return (12 <div className='my-4'>13 <h3 className='text-xl my-4'>Related Posts:</h3>14 <section className='flex flex-wrap justify-between gap-5'>15 {posts.map((post, idx) => {16 return <RelatedCard post={post} key={post.id} />;17 })}18 </section>19 </div>20 );21 };22 export default RelatedPosts;
Fetch the data with GraphQL Query
We’ve created the Related Posts component, and now we need to integrate it and pass the posts into it. We'll fetch the data using GraphQL in any parent component or template. For this example, we’ll use a post template.
1 export const query = graphql`2 query GetSinglePost($slug: String, $category: String) {3 mdx(frontmatter: { slug: { eq: $slug } }) {4 excerpt5 frontmatter {6 category7 date(formatString: "YYYY-MM-DDTHH:mm:ss.sssZ")8 slug9 title10 desc11 tags12 image {13 childImageSharp {14 gatsbyImageData(15 layout: CONSTRAINED16 formats: WEBP17 webpOptions: { quality: 50 }18 width: 80019 )20 }21 }22 }23 fields {24 timeToRead {25 minutes26 }27 }28 tableOfContents29 }30 relatedPosts: allMdx(31 filter: {32 frontmatter: { type: { eq: "post" }, category: { eq: $category }, slug: { ne: $slug } }33 }34 limit: 435 sort: { frontmatter: { date: DESC } }36 ) {37 nodes {38 id39 frontmatter {40 title41 permalink42 desc43 }44 }45 }46 }47 `;
This GraphQL query is fetching data for a single blog post and its related posts in a Gatsby project. Here's a breakdown of what each part of the code does:
- graphql: A template literal for writing GraphQL queries in Gatsby.
- GetSinglePost: The name of the query. It accepts two variables:
$slug
(a unique identifier for the post) and$category
(the category the post belongs to).
Fetching the Main Post (mdx):
1 mdx(frontmatter: { slug: { eq: $slug } }) {
- This part retrieves a single post where the
slug
(a unique identifier for the post) matches the value of$slug
.
Inside this block, it fetches several fields from the post:
- excerpt: A short summary or snippet of the post.
- frontmatter: Contains metadata about the post, such as:
- category: The category of the post.
- date: The publication date, formatted as YYYY-MM-DDTHH:mm:ss.sssZ.
- slug: The unique slug of the post.
- title: The title of the post.
- desc: A description of the post.
- image: Fetches the post’s image, including image optimization options like:
- gatsbyImageData: Generates optimized images using the gatsby-plugin-image API.
- fields: Includes the calculated timeToRead, specifically fetching the reading time in minutes.
- tableOfContents: Retrieves the post’s table of contents if it exists.
Fetching Related Posts:
1 relatedPosts: allMdx(2 filter: {3 frontmatter: { type: { eq: "post" }, category: { eq: $category }, slug: { ne: $slug } }4 }5 limit: 46 sort: { frontmatter: { date: DESC } }7 ) {
- relatedPosts: This alias fetches related posts based on their category and excludes the current post
(slug: { ne: $slug })
. It filters posts by: - type: "post": Ensures only posts (not pages or other content types) are returned.
- category:
{ eq: $category }
: Filters posts by the same category as the current post. - slug:
{ ne: $slug }
: Excludes the current post from the results. - The query limits the related posts to a maximum of 4 (limit: 4).
- The results are sorted by the date field in descending order (date: DESC), showing the most recent posts first.
Purpose:
The query is designed to fetch detailed information about a specific blog post and a list of related posts based on the same category. This is often used to display related posts at the end of a blog article to enhance user engagement.
Alternative Method: Filter the data with Javascript
The fastest way to fetch related posts in terms of performance is by using a GraphQL query. But what if we need more refined sorting for our posts? That’s where native JavaScript comes in handy.
What have we done so far?
We’ve been pulling the latest posts from a category using GraphQL. While this method is quick, it's not ideal for SEO because all posts end up linking to the same 4 recent posts. Worse, these links change with every deployment, which isn’t great for search rankings.
To improve this, we’ll sort our posts by date and only show the previous posts within the same category. The downside of this approach is that the first post won’t have any related posts, and the second post will only have one. However, this method provides static links that won’t change every time new content is added, which is much better for SEO.
1 const PostTemplate: React.FC<PageProps<Queries.GetSinglePostQuery>> = ({2 data: {3 mdx: {4 frontmatter: { title, category, image, date, tags },5 fields: {6 timeToRead: { minutes },7 },8 tableOfContents,9 },10 relatedPosts,11 },12 children,13 }) => {14 // Fetching all posts with a hook15 const { posts } = useAllPosts();1617 // Filtering all posts by category18 const filteredPostsByCategory = posts.filter((post) => post.frontmatter.category === category);19 // Filtering all posts by date20 let filteredRelatedPosts = filteredPostsByCategory.filter(21 (post) => Date.parse(post.frontmatter.date) < Date.parse(date),22 );23 if (filteredRelatedPosts.length > 4) {24 filteredRelatedPosts = filteredRelatedPosts.slice(0, 4);25 }2627 return (28 <Layout>29 // Component code3031 <RelatedPosts posts={filteredRelatedPosts} />32 </Layout>33 );34 };
The goal of the component is to display the main post's content and show related posts based on the same category, while ensuring the related posts are older (published earlier) than the current post. By limiting the number of related posts to a maximum of 4, the component ensures a clean and consistent UI.
This approach avoids the issue of displaying the most recent posts repeatedly, providing static, date-based related content instead.
Final Thoughts
Creating effective related posts in your Gatsby blog can significantly improve user engagement, SEO, and page views. By carefully choosing relevant content, limiting the number of posts, and presenting them in an appealing way, you can help readers discover more of your valuable content. The power of Gatsby’s GraphQL and React components gives you all the tools you need to create dynamic, user-friendly related post sections tailored to your audience’s needs.