Claude
Skills
Sign in
Back

content-collections

Included with Lifetime
$97 forever

Production-tested setup for Content Collections - a TypeScript-first build tool that transforms local content files (Markdown/MDX) into type-safe data collections with automatic validation. Use when: building blogs, documentation sites, or content-heavy applications with Vite + React, setting up MDX content with React components, implementing type-safe content schemas with Zod, migrating from Contentlayer, or encountering TypeScript import errors with content collections. Covers: Vite plugin setup, tsconfig path aliases, collection schemas with Zod validation, MDX compilation with compileMDX, transform functions for computed properties, rehype/remark plugins, React component integration with MDXContent, Cloudflare Workers deployment, and production build optimization. Keywords: content-collections, @content-collections/core, @content-collections/vite, @content-collections/mdx, MDX, markdown, Zod schema validation, type-safe content, frontmatter, compileMDX, defineCollection, defineConfig, Vite plugin, tsconfig paths, .content-collections/generated, MDXContent component, rehype plugins, remark plugins, content schema, document transform, allPosts import, static site generation, blog setup, documentation, Cloudflare Workers static assets, content validation errors, module not found content-collections, path alias not working, MDX type errors, transform function async, collection not updating

Design

What this skill does


# Content Collections

**Status**: Production Ready ✅
**Last Updated**: 2025-11-07
**Dependencies**: None
**Latest Versions**: @content-collections/[email protected], @content-collections/[email protected], [email protected]

---

## 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**:
1. Define collections in `content-collections.ts` (name, directory, Zod schema)
2. CLI/plugin scans filesystem, parses frontmatter, validates against schema
3. Generates TypeScript modules in `.content-collections/generated/`
4. 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

```bash
pnpm add -D @content-collections/core @content-collections/vite zod
```

### 2. Configure TypeScript Path Alias

Add to `tsconfig.json`:

```json
{
  "compilerOptions": {
    "paths": {
      "content-collections": ["./.content-collections/generated"]
    }
  }
}
```

###

 3. Configure Vite Plugin

Add to `vite.config.ts`:

```typescript
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:

```typescript
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

```bash
mkdir -p content/posts
```

Create `content/posts/first-post.md`:

```markdown
---
title: My First Post
date: 2025-11-07
description: Introduction to Content Collections
---

# My First Post

Content goes here...
```

### 7. Import and Use

```typescript
import { allPosts } from "content-collections";

console.log(allPosts); // Fully typed!
```

**Result**: Type-safe content with autocomplete, validation, and HMR.

---

## Critical Rules

### ✅ Always Do:

1. **Add path alias to tsconfig.json** - Required for imports to work
2. **Add .content-collections to .gitignore** - Generated files shouldn't be committed
3. **Use Standard Schema validators** - Zod, Valibot, ArkType supported
4. **Include `content` field in schema** - Required for frontmatter parsing
5. **Await compileMDX in transforms** - MDX compilation is async
6. **Put contentCollections() after react() in Vite** - Plugin order matters

### ❌ Never Do:

1. **Commit .content-collections directory** - Always generated, never committed
2. **Use non-standard validators** - Must support StandardSchema spec
3. **Forget to restart dev server after config changes** - Required for new collections
4. **Use sync transforms with async operations** - Transform must be async
5. **Double-wrap path alias** - Use `content-collections` not `./content-collections`
6. **Import from wrong package** - `@content-collections/core` for config, `content-collections` for 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`:
```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**:

1. Add to `.gitignore`:
```
.content-collections/
```

2. Add to `vite.config.ts` (if still happening):
```typescript
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:

```typescript
const posts = defineCollection({
  name: "posts",
  // ... schema
  transform: (post): PostWithSlug => ({ // Type the return!
    ...post,
    slug: post._meta.path.replace(/\.md$/, ""),
  }),
});

type PostWithSlug = {
  // ... schema fields
  slug: string;
};
```

**Source**: GitHub Issue #396

---

### Issue #4: Collection Not Updating on File Change

**Error**: New content files not appearing in collection.

**Why it happens**: Glob pattern doesn't match, or dev server needs restart.

**Prevention**:

1. Verify glob pattern matches your files:
```typescript
include: "*.md"        // Only root files
include: "**/*.md"     // All nested files
include: "posts/*.md"  // Only posts/ folder
```

2. Restart dev server after adding new files outside watched patterns
3. Check file actually saved (watch for editor issues)

**Source**: Common user error

---

### Issue #5: MDX Type Errors with Shiki

**Error**: `esbuild errors with shiki langAlias` or compilation failures.

**Why it happens**: Version incompatibility between Shiki and Content Collections.

**Prevention**:

Use compatible versions:

```json
{
  "devDependencies": {
    "@content-collections/mdx": "^0.2.2",
    "shiki": "^1.0.0"
  }
}
```

Check official compatibility matrix in docs before upgrading Shiki.

**Source**: GitHub Issue #598 (Next.js 15)

---

### Issue #6: Custom Path Aliases in MDX Imports Fail

**Error**: MDX imports with `@` alias don't resolve.

**Why it happens**: MDX compiler doesn't respect tsconfig path aliases.

**Prevention**:

Use relative paths in MDX imports:

```mdx
<!-- ❌ Won't work -->
import Component from "@/components/Component"

<!-- ✅ Works -->
import Component from "../../components/Component"
```

Or configure files appender (advanced, see references/transform-cookbook.md).

**Source**: GitHub Issue #547

---

### Issue #7: Unclear Validation Error Messages

**Error**: Cryptic Zod validation errors like "Expected string, received undefined".

**Why it happens**: Zod errors aren't formatted for content context.

**Prevention**:

Add custom error messages to schema:

```typescript
schema: z.object({
  title: z.string({
    required_error: "Title is required in frontmatter",
    invalid_type_error: "Title must be a string",
  }),
  date: z.string().refine(
    (val) => !isNaN(Date.parse(val)),
    "Date must be valid ISO date (YYYY-MM-DD)"
  ),
})
```

**Source**: GitHub Issue #403

---

### Issue #8: Ctrl+C Doesn't Stop Process

**Error**: Dev process hangs on exit, requires `kill -9`.

**Why it happens**: File watcher not cleaning up properly.

**Prevention**:

This is a known issue with the watcher. Workarounds:

1. Use `kill -9 <pid>` when it hangs
2. Use `content-collections watch` separately (not plugin) for more control
3. Add cleanup handler in `vite.config.ts` (advanced)

**Source**: GitHub Issue #546

---

## Configuration Patterns

### Basic Blog Collection

```typescript
import { defineCollection, defineConfig } from "@content-collections/core";
import { z } from "zod";

const posts = define
Files: 4
Size: 56.5 KB
Complexity: 49/100
Category: Design

Related in Design