JavaScript Development Space

Crafting Effective Related Posts for Your Gatsby Blog

Add to your RSS feed17 October 20247 min read
Crafting Effective Related Posts for Your Gatsby Blog

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.

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.

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.

mkdir related-posts
tsx
1 // related-card.types.tsx
2
3 export type RelatedPost = {
4 frontmatter: {
5 desc: string;
6 permalink: string;
7 title: string;
8 }
9 id: string;
10 }

We’ll use the card component from Shadcn. First, instantiate the component:

npx shadcn@latest add card
tsx
1 // related-card.tsx
2
3 import React from 'react';
4 import { Link } from 'gatsby';
5
6 import { Card, CardContent, CardTitle } from '../../ui/card';
7 import { RelatedPost } from './related-card.types';
8
9 type RelatedCardPostProps = {
10 post: RelatedPost;
11 };
12
13 const RelatedCard = ({ post }: RelatedCardPostProps) => {
14
15 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

tsx
1 import React from 'react';
2
3 import { RelatedPost } from './cards/related-card.types';
4 import RelatedCard from './cards/related-card';
5
6 type RelatedPostsProps = {
7 posts: RelatedPost[];
8 };
9
10 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.

js
1 export const query = graphql`
2 query GetSinglePost($slug: String, $category: String) {
3 mdx(frontmatter: { slug: { eq: $slug } }) {
4 excerpt
5 frontmatter {
6 category
7 date(formatString: "YYYY-MM-DDTHH:mm:ss.sssZ")
8 slug
9 title
10 desc
11 tags
12 image {
13 childImageSharp {
14 gatsbyImageData(
15 layout: CONSTRAINED
16 formats: WEBP
17 webpOptions: { quality: 50 }
18 width: 800
19 )
20 }
21 }
22 }
23 fields {
24 timeToRead {
25 minutes
26 }
27 }
28 tableOfContents
29 }
30 relatedPosts: allMdx(
31 filter: {
32 frontmatter: { type: { eq: "post" }, category: { eq: $category }, slug: { ne: $slug } }
33 }
34 limit: 4
35 sort: { frontmatter: { date: DESC } }
36 ) {
37 nodes {
38 id
39 frontmatter {
40 title
41 permalink
42 desc
43 }
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):

js
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:

js
1 relatedPosts: allMdx(
2 filter: {
3 frontmatter: { type: { eq: "post" }, category: { eq: $category }, slug: { ne: $slug } }
4 }
5 limit: 4
6 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.

tsx
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 hook
15 const { posts } = useAllPosts();
16
17 // Filtering all posts by category
18 const filteredPostsByCategory = posts.filter((post) => post.frontmatter.category === category);
19 // Filtering all posts by date
20 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 }
26
27 return (
28 <Layout>
29 // Component code
30
31 <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.

JavaScript Development Space

© 2024 JavaScript Development Space - Master JS and NodeJS. All rights reserved.