In this post, I will walk you through how I added ‘Other Posts you may like’ section in astro blog. Initially I wanted to have a ‘similar posts’ section, which would display posts which have same tags.
But why to have similar posts section?
While visting several blogs, I observed that I am reading more posts if there’s a list below compared to me going to all posts page and then reading them. Hence I thought to add one to my blog as well.
Iteration 1
First I added one function which gets all posts, filters them based on tag and then removes the post which we are already viewing.
export async function similarPosts(tag: string, excludingTitle: string = '') {
// get all posts
const blogPostList = await getBlogList()
// filter posts based on tag
let similarPosts = blogPostList.filter((post) => post.data.tags?.includes(tag))
// remove post based on title
if (excludingTitle !== '') return similarPosts = similarPosts.filter(post => post.data.title !== excludingTitle)
return similarPosts
}
But it was looking incomplete. Some posts would have similar posts but many didn’t had any. So I changed the plan from ‘similar posts’ to ‘other posts you may like’.
With that, if there are less or no similar posts then I could display few recent posts in the list.
Iteration 2
I added this function
export async function processedSimilarPosts(tags: string[], excludingTitle: string = '') {
const MINIMUM_NUMBER_OF_SIMILAR_POSTS = 3
let otherPosts: any[] = []
// get similar posts of all tags
for (const tag of tags) {
const blogPostList = await similarPosts(tag, excludingTitle)
otherPosts = [...otherPosts, ...blogPostList]
}
// return posts if they are more than decided count
if (otherPosts.length >= MINIMUM_NUMBER_OF_SIMILAR_POSTS) return otherPosts
const remainingPostsToFill = Math.abs(otherPosts.length - MINIMUM_NUMBER_OF_SIMILAR_POSTS)
// again get all posts
let additionalPosts = await getBlogList()
// and then filter them if it is current post OR if it is already present in from the tag
additionalPosts = additionalPosts.filter(post => {
if (post.data.title === excludingTitle) return false
const postExistsInOtherPosts = otherPosts.some(otherPost => otherPost.data.slug === post.data.slug);
return !postExistsInOtherPosts;
})
// take only the required number of posts
additionalPosts.splice(remainingPostsToFill)
const combinedPosts = [...otherPosts, ...additionalPosts]
return combinedPosts
}
Iteration 3
Later, I also added this function to remove duplicates because one post could have multiple tags.
function removeDuplicatePosts(posts: any[]) {
const titleMap = new Map<string, any>()
for (const post of posts) {
const postTitle = post.data.title
if (!titleMap.has(postTitle)) {
titleMap.set(postTitle, post)
}
}
const uniquePosts = Array.from(titleMap.values())
return uniquePosts
}
Now, I could just call processedSimilarPosts function and map over the result to display posts.
Live example below 👇