content-collections
ClaudeSkillz: For when you need skills, but lazier
npx skills add https://github.com/jackspace/claudeskillz --skill content-collectionsSKILL.md
Content Collections
Status: Production Ready ✅ Last Updated: 2025-11-07 Dependencies: None Latest Versions: @content-collections/core@0.12.0, @content-collections/vite@0.2.7, zod@3.23.8
What is Content Collections?
Content Collections transforms local content files (Markdown/MDX) into type-safe TypeScript data with automatic validation at build time.
Problem it solves: Manual content parsing, lack of type safety, runtime errors from invalid frontmatter.
How it works:
- Define collections in
content-collections.ts(name, directory, Zod schema) - CLI/plugin scans filesystem, parses frontmatter, validates against schema
- Generates TypeScript modules in
.content-collections/generated/ - Import collections:
import { allPosts } from "content-collections"
Perfect for: Blogs, documentation sites, content-heavy apps with Cloudflare Workers, Vite, Next.js.
Quick Start (5 Minutes)
1. Install Dependencies
pnpm add -D @content-collections/core @content-collections/vite zod
2. Configure TypeScript Path Alias
Add to tsconfig.json:
{
"compilerOptions": {
"paths": {
"content-collections": ["./.content-collections/generated"]
}
}
}
- Configure Vite Plugin
Add to vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import contentCollections from "@content-collections/vite";
export default defineConfig({
plugins: [
react(),
contentCollections(), // MUST come after react()
],
});
4. Update .gitignore
.content-collections/
5. Create Collection Config
Create content-collections.ts in project root:
import { defineCollection, defineConfig } from "@content-collections/core";
import { z } from "zod";
const posts = defineCollection({
name: "posts",
directory: "content/posts",
include: "*.md",
schema: z.object({
title: z.string(),
date: z.string(),
description: z.string(),
content: z.string(),
}),
});
export default defineConfig({
collections: [posts],
});
6. Create Content Directory
mkdir -p content/posts
Create content/posts/first-post.md:
---
title: My First Post
date: 2025-11-07
description: Introduction to Content Collections
---
# My First Post
Content goes here...
7. Import and Use
import { allPosts } from "content-collections";
console.log(allPosts); // Fully typed!
Result: Type-safe content with autocomplete, validation, and HMR.
Critical Rules
✅ Always Do:
- Add path alias to tsconfig.json - Required for imports to work
- Add .content-collections to .gitignore - Generated files shouldn't be committed
- Use Standard Schema validators - Zod, Valibot, ArkType supported
- Include
contentfield in schema - Required for frontmatter parsing - Await compileMDX in transforms - MDX compilation is async
- Put contentCollections() after react() in Vite - Plugin order matters
❌ Never Do:
- Commit .content-collections directory - Always generated, never committed
- Use non-standard validators - Must support StandardSchema spec
- Forget to restart dev server after config changes - Required for new collections
- Use sync transforms with async operations - Transform must be async
- Double-wrap path alias - Use
content-collectionsnot./content-collections - Import from wrong package -
@content-collections/corefor config,content-collectionsfor data
Known Issues Prevention
Issue #1: Module not found: 'content-collections'
Error: Cannot find module 'content-collections' or its corresponding type declarations
Why it happens: Missing TypeScript path alias configuration.
Prevention:
Add to tsconfig.json:
{
"compilerOptions": {
"paths": {
"content-collections": ["./.content-collections/generated"]
}
}
}
Restart TypeScript server in VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server"
Source: Common user error
Issue #2: Vite Constant Restart Loop
Error: Dev server continuously restarts, infinite loop.
Why it happens: Vite watching .content-collections directory changes, which triggers regeneration.
Prevention:
- Add to
.gitignore:
.content-collections/
- Add to
vite.config.ts(if still happening):
export default defineConfig({
server: {
watch: {
ignored: ["**/.content-collections/**"],
},
},
});
Source: GitHub Issue #591 (TanStack Start)
Issue #3: Transform Types Not Reflected
Error: TypeScript types don't match transformed documents.
Why it happens: TypeScript doesn't automatically infer transform function return type.
Prevention:
Explicitly type your transform return:
const posts = defineCollection({
name: "posts",
...