- Published on
- ยท8 min read
Adding Related Post List to Next.js Blog Posts
I apologize in advance for any awkward expressions in English. ๐
English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.
Overview
In the Tailwind CSS Blog Starter template I use, there's a default feature to display Previous
and Next
posts.
However, I found "Previous" and "Next" posts to be less meaningful for my needs, so instead of displaying them, I decided to show a list of Related Posts
based on the tags used in the current post.
Preview
Implementation
1. Filtering Related Posts
We will filter a list of 5 posts that share the same tags as the current post.
Open the
pages/blog/[...slug].js
file.Add the
getRelatedPosts
function.
import kebabCase from '@/lib/utils/kebabCase'
function getRelatedPosts(allPosts, currentPost) {
const defaultCount = 5
return allPosts
.filter(
(item) =>
item.draft !== true &&
item.slug !== currentPost.frontMatter.slug &&
item.tags.filter((tag) =>
currentPost.frontMatter.tags.map((m) => kebabCase(m)).includes(kebabCase(tag))
).length > 0
)
.slice(0, defaultCount)
}
- Exclude posts with draft set to true.
- Exclude the current post.
- Filter posts that contain tags identical to those of the current post.
- Filter only the latest 5 posts from this list.
- Modifying the
getStaticProps
Function.
Call the getRelatedPosts
function we added earlier, store the result in the relatedPosts
variable, and add it to the props
.
export async function getStaticProps({ params }) {
const allPosts = await getAllFilesFrontMatter('blog')
// ... omitted
const relatedPosts = getRelatedPosts(allPosts, post) // <- add
// ... omitted
return { props: { post, authorDetails, prev, next, relatedPosts } } // <- add relatedPosts
}
- Modifying
Blog
Component
Pass the relatedPosts
added to the props to the Blog
component.
export default function Blog({ post, authorDetails, prev, next, relatedPosts }) { // <- add relatedPosts
const { mdxSource, toc, frontMatter } = post
return (
<>
{frontMatter.draft !== true ? (
<MDXLayoutRenderer
layout={frontMatter.layout || DEFAULT_LAYOUT}
toc={toc}
mdxSource={mdxSource}
frontMatter={frontMatter}
authorDetails={authorDetails}
prev={prev}
next={next}
relatedPosts={relatedPosts} {/* <- add relatedPosts */}
/>
) : (
<div className="mt-24 text-center">...omitted</div>
)}
</>
)
}
2. Add the RelatedPosts Component
Create a new file named
RelatedPosts.js
in thecomponents
folder.Add the following content to the file. You can customize the UI according to your preference.
import Link from 'next/link'
import siteMetadata from '@/data/siteMetadata'
const RelatedPosts = ({ posts }) => {
if (!posts || posts.length === 0) return null
return (
<div className="my-2 rounded-lg bg-gray-100 py-4 pl-4 pr-4 dark:bg-gray-800 xl:py-8">
<h2 className="toc-ignore mb-2 text-base font-bold uppercase tracking-wide text-gray-800 dark:text-gray-200">
Related Tags Posts
</h2>
{posts.map((post, index) => (
<div key={post.slug} className="py-2">
<div className="flex flex-row content-center justify-start space-x-6">
<div className="min-w-[80px] text-xs font-semibold text-rose-400">
{new Date(post.date).toLocaleDateString(siteMetadata.locale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})}
</div>
<Link key={index} href={`/blog/${post.slug}`}>
<a className="truncate text-sm font-semibold text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300">
{post.title}
</a>
</Link>
</div>
</div>
))}
</div>
)
}
export default RelatedPosts
3. Add the RelatedPosts Component to the Desired Layout
Open the layout file where you want the RelatedPosts to be displayed. (I added it to the default layout I previously created,
layouts/PostToc.js
).Place the RelatedPosts component where you want it to appear.
import RelatedPosts from '@/components/RelatedPosts' // add
const postDateTemplate = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
export default function PostLayout({ frontMatter, authorDetails, children, relatedPosts }) { // <- add relatedPosts
const { slug, date, title, tags } = frontMatter
return (
<SectionContainer>
// ...
<ScrollTopAndComment />
<article>
<div className="...">
<div className="..." style={{ gridTemplateRows: 'auto 1fr' }}
>
<div className="...">
<div className="...">{children}</div>
<Comments frontMatter={frontMatter} />
<RelatedPosts posts={relatedPosts} /> {/* <- add RelatedPosts */}
</div>
</div>
</div>
</article>
</SectionContainer>
)
}
Code
You can review the code on the related_post branch or by checking the commit history on GitHub.