komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Content & MDX••By Komal Nakrani

Fix: Content Schema Should Not Contain slug in Astro

Astro error 'Content schema should not contain slug': slug is reserved for entry ids. Remove it from the Zod schema; keep it in frontmatter.

#Astro#Errors#Content Collections#Zod

Astro shows “Content schema should not contain slug” when a collection’s Zod schema includes a slug field. The message explains why: slug “is reserved for slug generation.” Astro reads a slug value from frontmatter itself and uses it as the entry’s id, so it can’t also be validated as ordinary data. Delete slug from the schema. You can keep writing slug: in frontmatter, and it will still set the URL.

Key Takeaways

  • Remove slug from z.object({ ... }).
  • A slug in frontmatter still works: it becomes the entry’s id.
  • Read it as entry.id, not entry.data.slug.
  • If you need a separate URL-like field, name it something else, like path or permalink.

Before and after

// Wrong
const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    slug: z.string(),
  }),
});
// Right
const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
  }),
});

The Markdown file keeps its frontmatter:

---
title: "Introducing our new pricing"
slug: pricing-2026
---

Using the slug in pages

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.id }, // "pricing-2026"
    props: { post },
  }));
}

Code that read post.data.slug should read post.id. If there’s no slug in frontmatter, the id comes from the file path.

Validating slugs anyway

You lose Zod validation on slug, but you can still enforce a format with a small check in getStaticPaths() or a script in CI:

for (const post of posts) {
  if (!/^[a-z0-9-]+(\/[a-z0-9-]+)*$/.test(post.id)) {
    throw new Error(`Bad URL id "${post.id}" in ${post.filePath}`);
  }
}

That catches capital letters and spaces before they become awkward URLs.

When you need a second URL field

Some projects store a legacy URL for redirects. Use a different name:

schema: z.object({
  title: z.string(),
  legacyPath: z.string().optional(),
}),

Then generate redirects from it.

Related: two entries with the same slug cause Duplicate content entry slug. The Astro content collections guide covers ids and schemas, and the content docs for Komal themes show a theme’s schema. See also the Astro collections guide.

FAQ

Is slug deprecated in Astro?

The entry.slug property was replaced by entry.id. A slug frontmatter field is still supported for setting a custom id.

Can I use a different frontmatter key to set the id?

Yes, with generateId on the glob() loader. It receives the entry’s data and file path and returns the id.