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
relatedPosts
property 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
relatedPosts
frontmatter 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.