Blog
Learn how to create, manage, and customize blog posts with multi-language support
MkSaaS includes a powerful blog system built with Fumadocs MDX. The blog system supports multi-language content, categories, authors, and rich content formatting, making it ideal for SaaS marketing, announcements, tutorials, and knowledge base articles.
Blog System Structure
The blog system is built using Fumadocs MDX and integrates with internationalization.
Source Configuration
The blog system is configured using Fumadocs MDX in the source.config.ts
file:
import { defineCollections, frontmatterSchema } from 'fumadocs-mdx/config';
import { z } from 'zod';
// Blog posts collection
export const blog = defineCollections({
type: 'doc',
dir: 'content/blog',
schema: frontmatterSchema.extend({
image: z.string(),
date: z.string().date(),
published: z.boolean().default(true),
categories: z.array(z.string()),
author: z.string(),
}),
});
// Blog authors collection
export const author = defineCollections({
type: 'doc',
dir: 'content/author',
schema: z.object({
name: z.string(),
avatar: z.string(),
description: z.string().optional(),
}),
});
// Blog categories collection
export const category = defineCollections({
type: 'doc',
dir: 'content/category',
schema: z.object({
name: z.string(),
description: z.string().optional(),
}),
});
Then the blog posts, authors, and categories are loaded using Fumadocs loader in src/lib/source.ts
:
import { type InferPageType, loader } from 'fumadocs-core/source';
import { createMDXSource } from 'fumadocs-mdx';
import { author, blog, category } from '../../../.source';
import { docsI18nConfig } from './i18n';
/**
* Blog posts source
*/
export const blogSource = loader({
baseUrl: '/blog',
i18n: docsI18nConfig,
source: createMDXSource(blog),
});
/**
* Blog authors source
*/
export const authorSource = loader({
baseUrl: '/author',
i18n: docsI18nConfig,
source: createMDXSource(author),
});
/**
* Blog categories source
*/
export const categorySource = loader({
baseUrl: '/category',
i18n: docsI18nConfig,
source: createMDXSource(category),
});
// Type definitions
export type BlogType = InferPageType<typeof blogSource>;
export type AuthorType = InferPageType<typeof authorSource>;
export type CategoryType = InferPageType<typeof categorySource>;
Creating Blog Content
Adding a New Author
Create a new MDX file in the content/author
directory:
---
name: John Doe
avatar: /images/authors/john-doe.jpg
---
Adding a New Category
Create a new MDX file in the content/category
directory:
---
name: Tutorial
description: Step-by-step guides to learn new features
---
Adding a New Blog Post
Create a new MDX file in the content/blog
directory:
---
title: My First Blog Post
description: This is a brief description of my first blog post.
image: /images/blog/my-first-post.jpg
date: "2023-12-01"
published: true
categories: ["tutorial", "announcement"]
author: "mksaas"
---
# Introduction
This is my first blog post. Here I'll talk about something interesting.
## Section 1
Some content here...
## Section 2
More content here...
Multi-language Support
MkSaaS blog system fully supports internationalization. You can create content in multiple languages using the following file naming convention:
- Default locale (e.g., English):
filename.mdx
- Other locales (e.g., Chinese):
filename.zh.mdx
Multi-language Authors and Categories
Follow the same pattern for authors and categories:
---
name: MkSaaS 团队
avatar: /images/authors/mksaas.jpg
---
---
name: 公告
description: 官方平台公告和更新
---
Multi-language Blog Post Example
For an English blog post:
---
title: Welcome to our Blog
description: Our first official blog post
image: /images/blog/welcome.jpg
date: "2023-12-01"
published: true
categories: ["announcement"]
author: "mksaas"
---
Content in English...
For the same post in Chinese:
---
title: 欢迎来到我们的博客
description: 我们的第一篇官方博客文章
image: /images/blog/welcome.jpg
date: "2023-12-01"
published: true
categories: ["announcement"]
author: "mksaas"
---
Content in Chinese...
The system will automatically match the post with the appropriate language based on the user's locale.
Customizing the Blog Schema
To add new fields to blog posts, authors, or categories:
- Modify the schema in
source.config.ts
- Run the command
pnpm run content
to regenerate the.source
folder - Update components to display the new fields
Example: Add a "featured" Field
export const blog = defineCollections({
type: 'doc',
dir: 'content/blog',
schema: frontmatterSchema.extend({
image: z.string(),
date: z.string().date(),
published: z.boolean().default(true),
categories: z.array(z.string()),
author: z.string(),
// Add the new field
featured: z.boolean().default(false),
}),
});
Then, you can use this field in your blog posts:
---
title: Important Announcement
description: Read this important announcement
image: /images/blog/announcement.jpg
date: "2023-12-01"
published: true
categories: ["announcement"]
author: "mksaas"
featured: true
---
Content here...
Then, run the command
pnpm run contentto regenerate the
.source` folder.
Advanced Usage
Querying Posts Programmatically
You can query posts programmatically using the Fumadocs sources:
import { blogSource, authorSource, categorySource } from '@/lib/docs/source';
// Get all blog posts
const allPosts = blogSource.getPages();
// Get published posts
const publishedPosts = allPosts.filter(post => post.data.published);
// Get posts by category
const getPostsByCategory = (categorySlug: string) => {
return allPosts.filter(post =>
post.data.categories.includes(categorySlug)
);
};
// Get posts by author
const getPostsByAuthor = (authorSlug: string) => {
return allPosts.filter(post => post.data.author === authorSlug);
};
// Get all authors
const allAuthors = authorSource.getPages();
// Get all categories
const allCategories = categorySource.getPages();
Changing Blog Post Card Layout
Customize the blog card component in src/components/blog/blog-card.tsx
:
import type { BlogType } from '@/lib/docs/source';
interface BlogCardProps {
post: BlogType;
}
export function BlogCard({ post }: BlogCardProps) {
return (
<div className="group flex flex-col border rounded-lg overflow-hidden h-full bg-card shadow-sm hover:shadow-md transition-shadow">
{/* Access post data through post.data */}
<h3>{post.data.title}</h3>
<p>{post.data.description}</p>
<time>{post.data.date}</time>
{/* ... rest of the component */}
</div>
);
}
Build Process
The blog system uses Fumadocs MDX's build process:
- Development: Content is processed on-demand during development
- Build: Run
pnpm run content
to generate optimized content sources - Generated Files: The
.source
directory contains generated TypeScript files with your content
Best Practices
- Use High-Quality Images: Use properly sized and optimized images for blog posts
- Consistent Categories: Maintain a consistent set of categories across posts
- Strong Meta Content: Write compelling titles and descriptions for SEO benefits
- Structured Content: Use proper headings and sections in your blog post content
- Include Table of Contents: For longer posts, ensure headings are organized for TOC
- International Content: Keep translations consistent across all localized content
- Optimize Images: Use responsive images and lazy loading for better performance
- Schema Validation: Leverage Zod schemas for type-safe content validation
Next Steps
Now that you understand how to work with the blog system in MkSaaS, you might want to explore these related features: