List of All Blog Posts
Page with a list of all blog posts in the blog
Adding utility methods
Two new utility methods for formatting dates and sorting the posts are added in src/lib/utils.ts
:
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { Post } from "#site/content";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(input: string | number): string {
const date = new Date(input);
return date.toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
});
}
export function sortPosts(posts: Array<Post>) {
return posts.sort((a, b) => {
if (a.date > b.date) return -1;
if (a.date < b.date) return 1;
return 0;
});
}
Component with blog post information
Each individual blog post is displayed in the component src/components/components/post-item.tsx
:
import { Calendar } from "lucide-react";
import Link from "next/link";
import { buttonVariants } from "./ui/button";
import { cn, formatDate } from "@/lib/utils";
interface PostItemProps {
slug: string;
title: string;
description?: string;
date: string;
}
export function PostItem({ slug, title, description, date }: PostItemProps) {
return (
<article className="flex flex-col gap-2 border-border border-b py-3">
<div>
<h2 className="text-2xl font-bold">
<Link href={slug}>{title}</Link>
</h2>
</div>
<div className="max-w-none text-muted-foreground">{description}</div>
<div className="flex justify-between items-center">
<dl>
<dt className="sr-only">Published On</dt>
<dd className="text-sm sm:text-base font-medium flex items-center gap-1">
<Calendar className="h-4 w-4" />
<time dateTime={date}>{formatDate(date)}</time>
</dd>
</dl>
<Link
href={slug}
className={cn(buttonVariants({ variant: "link" }), "py-0")}
>
Read more →
</Link>
</div>
</article>
);
}
Blog list page
The page containing the list of blogs uses the component created earlier. src/app/blog/page.tsx
:
import { posts } from "#site/content";
import { PostItem } from "@/components/post-item";
import { sortPosts } from "@/lib/utils";
export default async function BlogPage() {
const sortedPosts = sortPosts(posts.filter((post) => post.published));
const displayPosts = sortedPosts;
return (
<div className="container max-w-4xl py-6 lg:py-10">
<div className="flex flex-col items-start gap-4 md:flex-row md:justify-between md:gap-8">
<div className="flex-1 space-y-4">
<h1 className="inline-block font-black text-4xl lg:text-5xl">Blog</h1>
<p className="text-xl text-muted-foreground">
Example Blog Title
</p>
</div>
</div>
<hr className="mt-8" />
{displayPosts?.length > 0 ? (
<ul className="flex flex-col">
{displayPosts.map((post) => {
const { slug, date, title, description } = post;
return (
<li key={slug}>
<PostItem
slug={slug}
date={date}
title={title}
description={description}
/>
</li>
);
})}
</ul>
) : (
<p>No blogs have been posted yet.</p>
)}
</div>
);
}
- Previous: Blog Post Page from MDX Content
- Next: Adding Metadata OpenGraph Image
Last Updated: