Source code viết lại có cả Post, Page để lấy thực hành
posts\post-1.mdx, posts\post-2.mdx
---
title: 'My First Blog Post 1'
date: '2024-03-08'
summary: 'This is a summary of my first blog post using Next.js and Contentlayer'
---
# Welcome to My Blog
This is the content of my first blog post. You can use all the power of Markdown here!
## Subheading
- List item 1
- List item 2
- List item 3
[Link to Next.js](https://nextjs.org)
In this tutorial, we’ll walk through the process of building a blog website using Next.js 14 and Contentlayer 2. You’ll learn how to seamlessly integrate Markdown files to create dynamic content, resulting in a fast, SEO-friendly, and easily maintainable blog.
Prerequisites
Basic knowledge of React and Next.js
Node.js installed on your machine
Setting Up the Project
1. Create a New Next.js Project
First, let’s create a new Next.js project:
npx create-next-app@latest my-markdown-blog
cd my-markdown-blog
This command creates a new Next.js project with the latest version (14 at the time of writing).
Defines a Post document type with required fields: title, date, and summary.
Sets up a computed url field for each post.
Configures Contentlayer to look for .mdx files in a posts directory.
Creating Content
Create a new directory called posts in your project root, and add a sample post:
---
title: "My First Blog Post"
date: '2024-03-08'
summary: "This is a summary of my first blog post using Next.js and Contentlayer."
---
# Welcome to My Blog
This is the content of my first blog post. You can use all the power of Markdown here!
## Subheading
- List item 1
- List item 2
- List item 3
[Link to Next.js](https://nextjs.org)
Displaying Posts on the Homepage
Update your src/app/page.tsx file:
import { allPosts, Post } from 'contentlayer/generated';
import { compareDesc, format, parseISO } from 'date-fns';
import Link from 'next/link';
function PostCard(post: Post) {
return (
<div className='mb-8'>
<h2 className='mb-1 text-xl'>
<Link href={post.url} className='text-blue-700 hover:text-blue-900'>
{post.title}
</Link>
</h2>
<time dateTime={post.date}>
{format(parseISO(post.date), 'LLLL d, yyyy')}
</time>
<p>{post.summary}</p>
</div>
);
}
export default function Home() {
const posts = allPosts.sort((a, b) =>
compareDesc(new Date(a.date), new Date(b.date))
);
return (
<div className='max-w-xl mx-auto my-8'>
<h1 className='text-center'>My Markdown Blog</h1>
{posts.map((post) => (
<PostCard {...post} key={post._id} />
))}
</div>
);
}
This code:
Imports all posts from Contentlayer’s generated files.
Sorts posts by date in descending order.
Renders a list of post cards, each linking to the full post.
Creating Individual Post Pages
Create a new file at src/app/posts/[slug]/page.tsx:
import Mdx from '@/components/mdx-components';
import { allPosts } from 'contentlayer/generated';
import { format, parseISO } from 'date-fns';
import { getMDXComponent } from 'next-contentlayer2/hooks';
interface PostPageProps {
params: {
slug: string;
};
}
export default function PostPage({ params }: PostPageProps) {
const post = allPosts.find((post) => post._raw.flattenedPath === params.slug);
if (!post?.body.code) {
return <div>Post not found</div>;
}
return (
<article className='py-8 mx-auto max-w-xl'>
<div className='mb-8 text-center'>
<time dateTime={post.date}>
{format(parseISO(post.date), 'LLLL d, yyyy')}
</time>
<h1>{post.title}</h1>
</div>
<Mdx code={post.body.code} />
</article>
);
}
This creates dynamic routes for each post and renders the post content.
Creating an MDX Component
Create a new file at src/components/mdx-components.tsx:
You now have a fully functional Markdown blog using Next.js 14 and Contentlayer 2! This setup provides a great foundation for a performant, easy-to-maintain blog. You can easily add more posts by creating new Markdown files in the posts directory.