Writings Static Website with Next JS Blog Post Page from MDX Content Blog post page reading content from the MDX markdown content through Velite and tailwindcss/typography
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;
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} />;
}
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 >
);
}
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 >
Last Updated: July 13, 2024