1. Cấu trúc file Markdown
Mỗi bài viết là 1 file .md trong src/data/blog/ với 2 phần:
Frontmatter (Metadata)
Phần metadata ở đầu file, giữa 2 dấu ---:
---
title: "Tiêu đề bài viết"
pubDatetime: 2025-10-21T10:00:00+07:00
description: "Mô tả ngắn gọn về bài viết"
tags: ["tag1", "tag2", "tag3"]
featured: false
draft: false
---
Content (Nội dung)
Phần nội dung dưới frontmatter, viết markdown bình thường:
## Heading 1
Đoạn văn bản thường...
### Heading 2
- List item 1
- List item 2
**Bold text** và *italic text*
[Link](https://example.com)
2. Các trường trong Frontmatter
Trường bắt buộc
- title:
string- Tiêu đề bài viết - pubDatetime:
date- Ngày xuất bản (ISO format:YYYY-MM-DDTHH:mm:ss+07:00) - description:
string- Mô tả ngắn (hiển thị trong preview, SEO)
Trường optional
- author:
string- Tác giả (default: lấy từSITE.authortrong config) - modDatetime:
date- Ngày chỉnh sửa lần cuối - tags:
string[]- Danh sách tags (default:["others"]) - featured:
boolean- Hiển thị trong featured posts (default:false) - draft:
boolean- Bản nháp, không publish (default:false) - ogImage:
string | image- Ảnh khi share lên social media - canonicalURL:
string- URL chuẩn cho SEO - hideEditPost:
boolean- Ẩn nút “Edit Post” - timezone:
string- Timezone (default: từSITE.timezone)
3. Schema Validation
File src/content.config.ts định nghĩa cấu trúc bắt buộc:
const blog = defineCollection({
loader: glob({ pattern: "**/[^_]*.md", base: "./src/data/blog" }),
schema: z.object({
author: z.string().default(SITE.author),
pubDatetime: z.date(),
modDatetime: z.date().optional().nullable(),
title: z.string(),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
tags: z.array(z.string()).default(["others"]),
ogImage: image().or(z.string()).optional(),
description: z.string(),
canonicalURL: z.string().optional(),
hideEditPost: z.boolean().optional(),
timezone: z.string().optional(),
}),
});
Loader pattern: **/[^_]*.md
- Quét tất cả file
.mdtrongsrc/data/blog/ - Bỏ qua file bắt đầu bằng
_(ví dụ:_draft.md)
4. Luồng xử lý của Astro
1. Tạo file .md trong src/data/blog/
↓
2. Astro đọc frontmatter → validate với schema
↓
3. Generate route /posts/[slug] (slug = tên file)
↓
4. Render markdown → HTML với PostDetails.astro layout
↓
5. User truy cập → thấy bài viết đã render
5. Layout Rendering
File src/layouts/PostDetails.astro xử lý việc render:
Load data từ frontmatter
const { title, author, description, pubDatetime, tags } = post.data;
Render markdown thành HTML
const { Content } = await render(post);
Hiển thị trong template
<article>
<Content /> <!-- Markdown content đã render -->
</article>
Tính năng tự động
- Copy button cho code blocks
- Heading links (click
#để share section) - Prev/next post navigation
- Archive month link
- Syntax highlighting cho code blocks
6. Ví dụ tạo bài viết mới
Bước 1: Tạo file
Tạo file src/data/blog/vi-du-bai-viet.md
Bước 2: Viết nội dung
---
title: "Ví dụ bài viết"
pubDatetime: 2025-10-21T10:00:00+07:00
description: "Đây là bài viết ví dụ"
tags: ["example", "tutorial"]
---
## Giới thiệu
Đây là nội dung bài viết...
### Code example
\`\`\`javascript
function hello() {
console.log("Hello World");
}
\`\`\`
### List example
- Item 1
- Item 2
- Item 3
Bước 3: Truy cập
Route tự động: /posts/vi-du-bai-viet
7. Tips
- Tên file nên dùng kebab-case (ví dụ:
cau-truc-file-blog.md) - Tên file sẽ thành slug trong URL
- File bắt đầu bằng
_sẽ bị bỏ qua (dùng cho draft) draft: truesẽ không hiển thị bài viết (nhưng vẫn build)pubDatetimephải đúng format ISO 8601- Tags nên lowercase, kebab-case
8. Tóm tắt
Astro Content Collections = Hệ thống quản lý blog posts
- File .md = 1 bài viết
- Frontmatter = Metadata (title, date, tags, …)
- Content = Markdown thuần
- Astro tự động: validate → generate routes → render HTML
Workflow: Tạo file .md → Astro tự động xử lý → Có route /posts/[slug]