← All posts

Two Astro gotchas I hit building this very blog

This blog is Astro content collections — three locales (en/ja/zh), one markdown file per language. Wiring it up, I hit two traps worth writing down. Partly because the fix isn’t obvious; partly because they make a fitting first real post.

Gotcha 1: a frontmatter slug is the entry id

I gave every post a slug field so the three language versions of the same story could share one URL:

lang: "en-us"
slug: "unify-mac-ios-downloads"

It built fine. But only the Chinese index listed the post — English and Japanese said “No posts yet.”

The glob loader treats a slug frontmatter field as the entry id. All three files declared the same slug, so they collapsed into a single entry, and the last one loaded (zh-tw, alphabetically) won. The other two simply vanished from the collection — no error, no warning.

Fix: drop the slug field. Let the id be the file path (en-us/unify-mac-ios-downloads), which is unique per locale, and derive the URL slug from the filename instead:

const slug = post.id.replace(/^[^/]+\//, ''); // strip the locale folder

Gotcha 2: getStaticPaths runs in its own scope

With the ids fixed, the next build failed harder:

slugOf is not defined

I’d factored that little replace into a helper at the top of the route file, then called it inside getStaticPaths. But getStaticPaths is special — Astro extracts and runs it in isolation, so it can’t see other top-level consts in the same frontmatter. The helper existed for the rest of the component; just not in there.

Fix: keep the derivation inline, or define it inside getStaticPaths:

export async function getStaticPaths() {
  const posts = (await getCollection('blog')).filter((p) => p.data.lang === 'en-us');
  return posts.map((post) => ({
    params: { slug: post.id.replace(/^[^/]+\//, '') },
    props: { post },
  }));
}

Both are the kind of trap that builds green on the happy path and only bites once you have more than one entry, or share a helper. Now you know — the blog you’re reading shipped right after.