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

Fix: Content Collection Is Missing a loader in Astro

Since Astro 6, every collection needs a loader. Fix 'Content collection is missing a loader definition' with glob() for folders or file() for JSON and YAML.

#Astro#Errors#Content Collections#Upgrade

Astro shows “Content collection is missing a loader definition” when a collection in src/content.config.ts is defined without a loader. Older Astro versions inferred collections from folders in src/content/; current versions require you to say where content comes from. The message: “Collections must have a loader defined.” Add glob() for a folder of files or file() for a single data file.

Key Takeaways

  • Every defineCollection() needs loader.
  • Folders of Markdown, MDX, JSON or YAML: glob({ pattern, base }).
  • One JSON or YAML file holding many entries: file('path').
  • Replace any type: 'content' or type: 'data' with a loader.

Before and after

Old style, which now errors:

// src/content.config.ts
const blog = defineCollection({
  type: 'content',
  schema: z.object({ title: z.string() }),
});

Current style:

// src/content.config.ts
import { defineCollection } from 'astro:content';
import { glob, file } 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() }),
});

const authors = defineCollection({
  loader: file('src/data/authors.json'),
  schema: z.object({ name: z.string() }),
});

export const collections = { blog, authors };

base is relative to the project root. The pattern above skips files starting with an underscore, a handy convention for drafts and partials.

Other changes that come with it

Projects hitting this error are usually upgrading, so expect these too:

  • The config file location. It must be src/content.config.ts. See Legacy content config file found.
  • entry.slug is now entry.id. Update links and getStaticPaths().
  • Rendering. Replace entry.render() with render(entry) imported from astro:content.
  • Deprecated helpers. getEntryBySlug() and getDataEntryById() become getEntry(); see Invalid use of getEntryBySlug.

Temporary escape hatch

legacy.collectionsBackwardsCompat: true in astro.config.mjs restores the old behaviour while you migrate. Use it to get a build passing, then remove it once collections have loaders.

Custom loaders

A loader doesn’t have to read files. An async function returning objects with an id works:

const releases = defineCollection({
  loader: async () => {
    const res = await fetch('https://api.example.com/releases');
    return (await res.json()).map((r: any) => ({ id: String(r.id), name: r.name }));
  },
  schema: z.object({ name: z.string() }),
});

The Astro content collections guide has a full migration checklist and loader examples, and the content docs for Komal themes show the config in a theme. The official upgrade notes are in the Astro v6 upgrade guide.

FAQ

Do I still need the src/content folder?

No. glob() can point anywhere with base. src/content/ remains a common convention.

What’s the difference between glob() and file()?

glob() makes one entry per file. file() makes many entries from one JSON or YAML file.