Content Collections
ใน Next.js คุณมักใช้ contentlayer หรือ fs + gray-matter เพื่อโหลดไฟล์ MDX พร้อม type safety Astro มี content collections built-in: วางไฟล์ MD/MDX ใน src/content/<collection>/ กำหนด schema ด้วย zod ใน src/content.config.ts แล้วใช้ getCollection('blog') ที่ไหนก็ได้ในโปรเจกต์ — มี type safety เต็มรูปแบบ ไม่ต้องใช้ library ของ third-party
ต้องใช้โปรเจกต์ Astro เต็มรูปแบบ ใช้
npm create astro@latestเพื่อสร้างโปรเจกต์
การกำหนด collection: contentlayer vs Astro
หัวข้อที่มีชื่อว่า “การกำหนด collection: contentlayer vs Astro”// contentlayer.config.ts (Next.js)import { defineDocumentType, makeSource } from 'contentlayer/source-files';
export const Post = defineDocumentType(() => ({ name: 'Post', filePathPattern: 'blog/**/*.mdx', contentType: 'mdx', fields: { title: { type: 'string', required: true }, date: { type: 'date', required: true }, tags: { type: 'list', of: { type: 'string' } }, },}));
export default makeSource({ contentDirPath: 'content', documentTypes: [Post] });
// In a page:// import { allPosts } from 'contentlayer/generated';// src/content.config.ts (Astro)import { defineCollection } from 'astro:content';import { glob } from 'astro/loaders';import { z } from 'astro/zod';
const blog = defineCollection({ loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }), schema: z.object({ title: z.string(), date: z.coerce.date(), tags: z.array(z.string()).optional(), }),});
export const collections = { blog };
// In a page:// import { getCollection } from 'astro:content';// const posts = await getCollection('blog');Schema อยู่ใน src/content.config.ts — Astro ตรวจสอบทุกไฟล์ใน collection ตอน build time และแสดง type errors ใน editor ของคุณ
สองรายละเอียดที่เปลี่ยนใน Astro 6/7 และ tutorial เก่า ๆ มักเขียนผิด: ทุก collection ต้องประกาศ loader (glob() สำหรับโฟลเดอร์ไฟล์, file() สำหรับไฟล์ JSON/YAML ไฟล์เดียว — ทั้งคู่มาจาก astro/loaders) แทน flag type: 'content' | 'data' แบบเดิม และ z ให้ import จาก astro/zod (Zod 4) แทน astro:content ส่วน entry ระบุด้วย entry.id (มาจากชื่อไฟล์) ส่วน entry.slug แบบเก่าถูกถอดออกแล้ว
content.config.ts
หัวข้อที่มีชื่อว่า “content.config.ts”import { defineCollection } from 'astro:content';import { glob } from 'astro/loaders';import { z } from 'astro/zod';
const blog = defineCollection({ // glob() loads a folder of MD/MDX; file() loads one JSON/YAML file loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }), schema: z.object({ title: z.string(), description: z.string().optional(), date: z.coerce.date(), draft: z.boolean().default(false), tags: z.array(z.string()).default([]), }),});
export const collections = { blog };การแสดงรายการ collection entries
หัวข้อที่มีชื่อว่า “การแสดงรายการ collection entries”---import { getCollection } from 'astro:content';
const posts = await getCollection('blog', ({ data }) => !data.draft);const sorted = posts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());---<html lang="en"> <head><meta charset="utf-8" /><title>Blog</title></head> <body> <h1>Blog</h1> <ul> {sorted.map((post) => ( <li> <a href={`/blog/${post.id}/`}>{post.data.title}</a> <time>{post.data.date.toLocaleDateString()}</time> </li> ))} </ul> </body></html>argument ที่สองของ getCollection คือ optional filter function — ที่นี่ใช้กรอง drafts ออก post.data มี type จาก zod schema เต็มรูปแบบ