Astro: Add Related Posts to Blog Entries
In this guide, I’ll walk through the process of adding a “Related Posts” feature to your Astro blog. By updating the content collection schema to include a new property and creating a custom component, you can easily link relevant blog posts within each entry.
Instructions
Add Related Posts Property to Content Collection Schema
Let’s start by updating the blog content collection schema in content.config.ts to include a relatedPosts property as an optional array of strings.
relatedPosts: z.array(z.string()).optional()
Now, each collection entry’s frontmatter can use the relatedPosts property to link related posts by their id or slug.
import { glob } from 'astro/loaders';
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
relatedPosts: z.array(z.string()).optional(),
}),
});
export const collections = { blog };
Create a Related Posts Component
To display links to related posts in an entry’s frontmatter, we create a new component called BlogPostRelatedPosts.astro.
AstroProject/
└── src/
└── components/
└── BlogPostRelatedPosts.astro
This component retrieves the blog collection and applies two filters:
- If the
relatedPostsproperty includes a reference to its ownslug, that link is excluded to avoid linking back to itself.
.filter((post) => post.id != Astro.params.slug)
- Only collection entries listed in the current entry’s
relatedPostsfrontmatter are included; all others are excluded.
.filter((post) => relatedPosts && relatedPosts.includes(post.id))
After applying the filters, the component displays a Related Posts heading with links and titles for each related post.
---
import { getCollection } from "astro:content";
const { relatedPosts } = Astro.props;
const posts = (await getCollection('blog'))
.filter((post) => post.id != Astro.params.slug)
.filter((post) => relatedPosts && relatedPosts.includes(post.id))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
{
(posts.length > 0) && (
<section>
<h2>Related Posts</h2>
{
posts.map((post) => (
<article>
<h2><a href={`/blog/${post.id}/`}>{post.data.title}</a></h2>
</article>
))
}
</section>
)
}
Add Component to Post Layout
To use the component, import BlogPostRelatedPosts into the blog layout file. In this example, <BlogPostRelatedPosts /> is placed after the main content to display the related post links before the footer.
---
import BlogPostRelatedPosts from '../components/BlogPostRelatedPosts.astro';
const { title, relatedPosts } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<main>
<article>
<h1>{title}</h1>
</article>
</main>
<BlogPostRelatedPosts relatedPosts={relatedPosts} />
</body>
</html>
Adjust Dynamic Route
Make sure the dynamic route for displaying collection entries exports a getStaticPaths() that returns an array of objects with a params property, including slug as post.id.
params: { slug: post.id }
In this example, [...slug].astro is updated as follows.
---
import BlogPost from '../../layouts/StandardPostLayout.astro';
import { getCollection } from 'astro:content';
import { render } from 'astro:content';
export async function getStaticPaths() {
const posts = (await getCollection('blog'))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
return posts.map((post) => ({
params: { slug: post.id },
props: post,
}));
}
const post = Astro.props;
const { Content } = await render(post);
---
<BlogPost {...post.data}>
<Content />
</BlogPost>
Add Related Posts to Frontmatter
Now that the code changes are complete, you can use the relatedPosts property in the frontmatter by adding id references to related posts in the array.
---
title: ''
pubDate: ''
relatedPosts: ['related-slug-1','related-slug-2']
---
Results
You can see how Related Posts works at the bottom of this post.
Summary
By adding a related posts property to the content collection schema and creating a custom component, you can easily display related posts within your Astro blog. This approach enhances user engagement by linking relevant content and ensuring a dynamic and organized display of related articles.