What Causes This Error?
This error occurs when a collection contains both content files (Markdown/MDX with body content) and data files (JSON/YAML without body content). Each collection must be either type: 'content' or type: 'data', not both.
The Problem
src/content/blog/
├── post.md # Content file (has body)
├── config.json # Data file (no body)
└── meta.yaml # Data file (no body)
// Trying to mix types in one collection
const blog = defineCollection({
type: 'content', // But folder has .json files too
schema: z.object({ /* ... */ }),
});
The Fix
Separate Into Different Collections
src/content/
├── blog/ # Content collection
│ ├── post-1.md
│ └── post-2.md
└── blog-meta/ # Data collection
├── config.json
└── authors.yaml
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
// Content collection for blog posts
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
date: z.date(),
}),
});
// Data collection for metadata
const blogMeta = defineCollection({
type: 'data',
schema: z.object({
siteName: z.string(),
postsPerPage: z.number(),
}),
});
export const collections = { blog, 'blog-meta': blogMeta };
Common Scenarios
Authors Data Separate from Posts
src/content/
├── posts/ # type: 'content'
│ ├── hello.md
│ └── world.md
└── authors/ # type: 'data'
├── john.json
└── jane.json
const posts = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
author: z.string(), // Reference to author ID
}),
});
const authors = defineCollection({
type: 'data',
schema: z.object({
name: z.string(),
email: z.string(),
bio: z.string(),
}),
});
Settings/Config Data
src/content/
├── pages/ # type: 'content'
│ └── about.md
└── settings/ # type: 'data'
└── navigation.json
const pages = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
}),
});
const settings = defineCollection({
type: 'data',
schema: z.object({
items: z.array(z.object({
label: z.string(),
url: z.string(),
})),
}),
});
Using Both Collections Together
---
import { getCollection, getEntry } from 'astro:content';
// Get content
const posts = await getCollection('posts');
// Get related data
const settings = await getEntry('settings', 'navigation');
// Combine them
const postsWithAuthors = await Promise.all(
posts.map(async (post) => {
const author = await getEntry('authors', post.data.author);
return { ...post, author: author.data };
})
);
---
If You Need Both in One Place
---
// Keep collections separate but use together
import { getCollection } from 'astro:content';
const blogPosts = await getCollection('blog');
const blogConfig = await getEntry('blog-settings', 'config');
const { postsPerPage } = blogConfig.data;
const paginatedPosts = blogPosts.slice(0, postsPerPage);
---
Collection Type Reference
| Type | File Extensions | Has Body | Example |
|---|---|---|---|
| content | .md, .mdx | Yes | Blog posts, docs |
| data | .json, .yaml, .toml | No | Config, authors |
Migration: Splitting Mixed Collection
# Before
src/content/blog/
├── post.md
└── settings.json
# After - move data files
mkdir -p src/content/blog-settings
mv src/content/blog/*.json src/content/blog-settings/
Quick Checklist
- Each collection is either
contentordata - Markdown/MDX files →
type: 'content' - JSON/YAML config →
type: 'data' - Split mixed folders into separate collections
- Reference between collections using IDs