用 frontmatter 的 slug 自訂文章網址


前言

在使用 Astro 建立部落格時,你一定會遇到一個需求:
👉 讓文章網址不是用檔名,而是用你自訂的 slug

例如:

/blog/astro-intro

而不是:

/blog/post-1

這篇文章會教你如何在 Astro 中,透過 frontmatter 的 slug 來控制文章網址。


🎯 最終效果

你在 Markdown 寫:

---
title: Astro 入門教學
slug: astro-intro
---

👉 網址會變成:

/blog/astro-intro

🧠 核心概念

Astro 預設是:

👉 用「檔名」當網址(slug)

但你可以改成:

👉 用「frontmatter 的 slug」當網址


🚀 實作步驟


🧩 Step 1:在 frontmatter 加入 slug

---
title: Astro 入門教學
slug: astro-intro
date: 2026-03-28T14:00:00
---

📁 Step 2:建立動態路由

建立檔案:

src/pages/blog/[slug].astro

👉 這代表網址會是:

/blog/xxx

⚙️ Step 3:用 slug 產生頁面

---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');

  return posts.map(post => ({
    params: {
      slug: post.data.slug, // 👈 使用 frontmatter 的 slug
    },
    props: {
      post,
    },
  }));
}

const { post } = Astro.props;
---

<h1>{post.data.title}</h1>

<article>
  <post.Content />
</article>

🔥 這段程式在做什麼?

slug: post.data.slug

👉 意思是:

「用 frontmatter 裡的 slug 當網址參數」


⚠️ 重要注意事項

❗slug 必須唯一

slug: astro-intro

👉 每篇文章都不能重複,否則會:

  • 路由衝突
  • build 失敗

❗建議加 schema 驗證(強烈推薦)

slug: z.string()

👉 避免忘記填 slug


❗沒寫 slug 會壞掉

如果你直接用:

post.data.slug

👉 沒寫 slug 的文章會:

  • 無法產生頁面
  • 或網址變成 undefined

🧠 改進做法(更穩定)

建議加 fallback:

---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');

  return posts.map(post => ({
    params: {
      slug: post.data.slug || post.slug, // 👈 fallback
    },
    props: { post },
  }));
}
---

🔥 最佳實務(推薦)

---
title: Astro 入門教學
slug: astro-intro
date: 2026-03-28T14:00:00
published: true
---

搭配:

slug: z.string()

🧠 常見問題:


Q:可以不用 slug 嗎?

可以 👉 Astro 會用檔名當網址


Q: slug 跟檔名哪個比較好?

方法優點缺點
檔名簡單不可控
slugSEO 佳 / 可控要維護

✅ 總結

在 Astro 中:

👉 用 slug 控制網址的步驟:

  1. 在 frontmatter 加 slug
  2. 建立 [slug].astro
  3. getStaticPaths() 指定 slug

🧠 一句話記住


👉 **「網址不是檔名,而是你定義的 slug」**

如果你下一步想進階:

  • 自動產生 slug(SEO friendly)
  • category + slug URL 結構
  • slug 變更自動 redirect

👉 這些都可以再往下優化 🚀 以上內容由 ChatGPT 產生


📖 後記

目前我的網站結構如下:

D:.
├───assets
├───components
├───content
│   └───blog
│       ├───2026032721600
│       ├───2026032743200
│       ├───2026032821600
│       ├───2026033021600
│       ├───2026033075600
│       └───ai-qna-records
├───layouts
├───pages
│   └───blog
└───styles

blog底下的資料夾,是用 自製小工具-時間戳記產生器 這篇的程式產生的。
文章的網址已經改由slug來決定,資料夾的名稱我是打算用來做文章的排序