How to parse MDX in Next.js using TypeScript? Part 2
Yet another strategy to store and parse MDX metadata in Next.js using TypeScript and without using any external database/CMS. continuedβ¦
Previously we had setup a basic directory structure for storing metadata of our posts. Also, we learnt how to transpile the typescript file and extract the metadata and return it as a list.
Now, let's use the metadata to render a list of posts.
First lets create a simple function that will use the metadata returned from readAllPosts()
function in crawler.ts
and provide neat APIs for us to use throughout the website.
/src/library/utils/postsRegistry.ts
import { Category, MetaData } from "@/app/types/metadata.types";
import { readAllPosts } from "./crawler";
export const PostRegistry = (() => {
const registry = new Map<string, MetaData>();
const refresh = async () => {
const allPosts = await readAllPosts();
const posts = allPosts.filter((post) => post.published);
posts.forEach((post) => {
if (registry.has(post.slug)) {
return;
}
registry.set(post.slug, post);
});
};
const getAll = async () => {
await refresh();
return Array.from(registry.values()).sort((a, b) => {
const dateA = a.updateDate || a.createDate;
const dateB = b.updateDate || b.createDate;
return dateB.getTime() - dateA.getTime();
});
};
const getAllByCategory = async (category: Category): Promise<MetaData[]> => {
return (await getAll()).filter((post) =>
post.categories.includes(category)
);
};
const getBySlug = async (slug: string): Promise<MetaData | undefined> => {
await refresh();
return registry.get(slug);
};
return {
getAll,
getBySlug,
getAllByCategory,
};
})();
In the above code, we create a map of all posts and their metadata indexed by slug
. We also pick the posts that are published i.e. published: true
. Next, we created a generic getAll
function that gets all the values in the registry and sort them by the updateDate
or createDate
of the post. We also created a getAllByCategory
which gets all the posts that have the given category. Finally, we created a getBySlug
which gets the post metadata by the given slug
.
Blog List Page
Now lets use the above APIs to build a page that lists all the posts. Create a new directory under /src/app/blog
and create a file post.tsx
with the following code.
Directory-Structure
.
π devy.in
|ββπ src
β βββπ app
β β βββπ blog
β β β βββπpost.tsx
....
src/app/blog/post.tsx
import { PostRegistry } from "@/library/api/post";
import { Box, Stack, Typography } from "@mui/material";
import { Metadata } from "next";
import Link from "next/link";
export default async function BlogHome() {
const allPosts = await PostRegistry.getAllByCategory("blog");
return (
<Stack direction="column" spacing={4}>
{allPosts.map((post) => (
<Box key={post.slug}>
<Link href={post.slug}>
<Typography variant="h4">{""+post.title}</Typography>
<Typography variant="body2" color="text.secondary">{post.description}</Typography>
</Link>
</Box>
))}
</Stack>
);
}
export const metadata: Metadata = {
title: "Blog",
description: "Blog posts"
};
A Voila!πͺ We have a blog list page.
Similarly, we can create a page for categories as well. You can make use of Next.js's dynamic routing /app/category/[category]/page.tsx
in combination with the APIs we created E.g. getAllByCategory
in the previous section to achieve this.