Blog
Learn how to write blog posts using Content Collections.
The starter kit uses Content Collections for managing blog content. All blog posts are written using .mdx files, which combine markdown with React components.
Why choose Content Collections? Content Collections is a great alternative to headless CMS platforms such as Contentful or Prismic. It's powered by MDX, free, open-source, and saves content directly in your repository.
The blog is configured in content-collections.ts and uses Fumadocs for rendering documentation-style content.
Add a new blog post
To create a new blog post, follow these steps:
-
Create a new file Navigate to the
content/postsdirectory and create a new.mdxfile. The file name will act as the URL slug for the post. For example:- File name:
hello-world.mdx - URL:
https://your-app.com/blog/hello-world
- File name:
-
Add metadata At the top of the
.mdxfile, include a frontmatter block. This block contains key metadata about your post, written in a YAML-like format enclosed by three dashes (---). Here's an example:
---
title: How to create a blog post
date: 2025-01-20T12:00:00.000Z
authorName: John Doe
authorImage: /authors/john.jpg
authorLink: https://example.com
excerpt: A short description of your blog post.
tags: [Innovation, Tutorial]
published: true
content: |
Your blog post content goes here...
---Frontmatter Fields
The blog post schema supports the following fields:
title(required) - The title of the blog postdate(required) - ISO 8601 date string for publication dateauthorName(required) - Name of the authorauthorImage(optional) - URL to author's imageauthorLink(optional) - Link to author's profileexcerpt(optional) - A short description/excerpt of the posttags(required) - Array of tag stringspublished(required) - Boolean to control visibilityimage(optional) - Featured image URLcontent(required) - The full content of the post
Using MDX Components
You can use React components directly in your MDX files. The starter kit provides several custom components:
---
title: Example Post
date: 2025-01-20T12:00:00.000Z
authorName: John Doe
tags: [Example]
published: true
content: |
# My Blog Post
<Callout className="mt-4">This is a callout component!</Callout>
Regular markdown content here.
---Code Blocks
Code blocks are automatically highlighted and support line numbers:
export function example() {
return 'Hello, World!';
}Images
You can include images in your blog posts:
Or use the Image component for more control:
import { Image } from '@/components/mdx-components';
<Image
src="/path/to/image.png"
alt="Alt text"
/>Building Content
Content Collections are automatically built during the Next.js build process. During development, they are rebuilt when files change.
Querying Blog Posts
You can query blog posts in your application:
import { allDocuments } from 'content-collections/generated';
export default function BlogPage() {
const posts = allDocuments('posts')
.filter((post) => post.published)
.sort((a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return dateB.getTime() - dateA.getTime();
});
return (
<div>
{posts.map((post) => (
<article key={post.path}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}Configuration
The blog collection is configured in content-collections.ts:
const posts = defineCollection({
name: 'posts',
directory: 'content/posts',
include: '**/*.{mdx,md}',
schema: z.object({
title: z.string(),
date: z.string(),
authorName: z.string(),
excerpt: z.string().optional(),
tags: z.array(z.string()),
published: z.boolean(),
content: z.string()
})
});
export default defineConfig({
collections: [posts]
});Best Practices
- Use descriptive filenames - The filename becomes the URL slug
- Add descriptions - Help with SEO and preview cards
- Use categories - Organize related posts
- Set publication dates - Control when posts appear
- Test locally - Always preview posts before publishing