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…
Posted On: Friday, 03-May-2024
How to parse MDX in Next.js using TypeScript? Part 2How to parse MDX in Next.js using TypeScript? Part 2

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.

Blog List PageBlog 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.

AN
Abhilash Nayak
Last Updated on: 03-05-2024

/> Devy.in© 2024Made from scratch with ❀️ & 🍡