Blog Post Page from MDX Content
Blog post page reading content from the MDX markdown content through Velite and tailwindcss/typography
Required dependencies
The tailwind typography module is used for displaying the blog content.
# npm i @tailwindcss/typography --save-dev
The typography module is then specified in the tailwind configuration file tailwind.config.ts
:
import type { Config } from "tailwindcss";
const config = {
darkMode: ["class"],
...
theme: {
...
extend: {
...
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/typography")
],
} satisfies Config;
export default config;
MDX component
The functionality for displaying MDX content in react is encapsulated in src/components/mdx-components.tsx
:
import Image from "next/image";
import * as runtime from "react/jsx-runtime";
const useMDXComponent = (code: string) => {
const fn = new Function(code);
return fn({ ...runtime }).default;
};
const components = {
Image,
};
interface MdxProps {
code: string;
}
export function MDXContent({ code }: MdxProps) {
const Component = useMDXComponent(code);
return <Component components={components} />;
}
Page for displaying an individual blog item
The MDX component created earlier is used on the page for displaying a blog post src/app/blog/[...slug]/page.tsx
:
import { posts } from "#site/content";
import { MDXContent } from "@/components/mdx-components";
import { notFound } from "next/navigation";
interface PostPageProps {
params: {
slug: string[];
};
}
async function getPostFromParams(params: PostPageProps["params"]) {
const slug = params?.slug?.join("/");
const post = posts.find((post) => post.slugAsParams === slug);
return post;
}
export async function generateStaticParams(): Promise<
PostPageProps["params"][]
> {
return posts.map((post) => ({ slug: post.slugAsParams.split("/") }));
}
export default async function PostPage({ params }: PostPageProps) {
const post = await getPostFromParams(params);
if (!post || !post.published) {
notFound();
}
return (
<article className="container py-6 prose dark:prose-invert max-w-3xl mx-auto">
<h1 className="mb-2">{post.title}</h1>
{post.description ? (
<p className="text-xl mt-0 text-muted-foreground">{post.description}</p>
) : null}
<hr className="my-4" />
<MDXContent code={post.body} />
</article>
);
}
Blog post URL
Once the change are done, the blog pages can be accessed at their respective relative path, for instnace http://localhost:3000/blog/2023/06-21-summer-solstice
.
Once the site is rebuilt the site map also contains entries for the new pages. With the entries created in the previous chapter we should observe in public\sitemap\sitemap-0.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://example.com</loc><lastmod>2024-07-02T15:43:23.213Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://example.com/about</loc><lastmod>2024-07-02T15:43:23.214Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://example.com/blog/2023/06-21-summer-solstice</loc><lastmod>2024-07-02T15:43:23.214Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://example.com/blog/2024/06-21-summer-solstice</loc><lastmod>2024-07-02T15:43:23.214Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
</urlset>
- Previous: Setting up Velite for Processing MDX Content
- Next: List of All Blog Posts
Last Updated: