Content Collections

repository·main·Indexed 22 days ago

https://github.com/sdorra/content-collections

A tool that transforms raw content files into type-safe data collections by automating data fetching, parsing, and validation. It supports Zod-based schemas and provides specialized adapters for Next.js, Qwik, Remix (Vite), Solid Start, SvelteKit, and Vite. Additional packages like @content-collections/markdown and @content-collections/mdx enable content transformation, while integration with ArkType allows for YAML file validation.

Tokens
45.6K
Snippets
148
Records
190
Agent score
75%

What's inside content-collections

  1. Access sibling documents in the same collection

    main

    To access other documents within the same collection (e.g., to find the 'previous' or 'next' post), use the collection.documents() function found on the collection object within the context.

    Important:

    • collection.documents() is asynchronous.
    • It returns an array of documents in their untransformed state (matching the schema shape).
    • Do not use the top-level documents() function to access the same collection; use collection.documents() instead.
    const posts = defineCollection({
      // ...
      transform: async (doc, { collection }) => {
        const docs = await collection.documents();
        const idx = docs.findIndex((d) => doc._meta.filePath === d._meta.filePath);
        return {
          ...doc,
          prev: idx > 0 ? docs[idx - 1] : null,
          next: idx < docs.length - 1 ? docs[idx + 1] : null,
        };
      },
    });
  2. Understand the structure of a Collection document

    main

    A collection (like allPosts) contains an array of documents. Each document follows the shape defined in your collection configuration.

    By default, each document includes a _meta property containing metadata such as the file path and file name, unless this property is explicitly removed during the transform step in your configuration.

  3. How Content Collections works

    main

    Content Collections transforms your content files into type-safe data collections. It works by generating an array of documents for each collection you define.

    Each document is a TypeScript object representing a file in your project. These documents are generated from files in a collection directory that match a specified glob pattern.

  4. Requirements for document serialization

    main

    When defining a collection, the resulting document object is serialized before being written to the file system. To ensure a document can be successfully stored and later read back into memory, the resulting object must be composed exclusively of serializable types. If your document contains non-serializable types (such as Functions, Symbols, or complex class instances), the serialization process will fail.

    Supported serializable types include:

    • String
    • Number
    • Boolean
    • Null
    • Undefined
    • Date
    • Map
    • Set
    • BigInt
    • Array containing only serializable types
    • Object containing only serializable values
  5. Access documents from other collections

    main

    You can join data from different collections by using the documents(collectionReference) function from the context object. This is useful for relational data, such as linking a post to an author.

    Details:

    • Pass the collection reference (e.g., authors) as an argument to documents().
    • The returned documents are untransformed (they follow the schema shape, not the transformed type).
    • This function is asynchronous.
    const posts = defineCollection({
      // ...
      transform: async (doc, { documents }) => {
        const author = await documents(authors).find(
          (a) => a.ref === doc.author
        );
        return {
          ...doc,
          author: author.displayName
        };
      },
    });
  6. Quickstart: Set up Content Collections

    main

    To use Content Collections, follow these three steps:

    1. Configure your collections: Create a content-collections.ts file at the root of your project. Use defineCollection to specify the collection name, directory, file pattern (include), and a Zod-based schema for validation. Use defineConfig to export the configuration.
    2. Add content: Create your content files (e.g., Markdown) in the directory specified in your configuration.
    3. Import and use: Import the generated collection (e.g., allPosts) directly from content-collections in your application components.
    // 1. content-collections.ts
    import { defineCollection, defineConfig } from "@content-collections/core";
    
    const posts = defineCollection({
      name: "posts",
      directory: "src/posts",
      include: "**/*.md",
      schema: (z) => ({
        title: z.string(),
        summary: z.string(),
        content: z.string(),
      }),
    });
    
    export default defineConfig({
      content: [posts],
    });
    
    // 2. src/posts/hello.md
    // ---
    // title: Hello World
    // summary: This is my first post
    // ---
    // # Hello World
    // This is my first post.
    
    // 3. App usage
    import { allPosts } from "content-collections";
    
    export function Posts() {
      return (
        <ul>
          {allPosts.map((post) => (
            <li key={post._meta.path}>
              <a href={`/posts/${post._meta.path}`}>
                <h3>{post.title}</h3>
                <p>{post.summary}</p>
              </a>
            </li>
          ))}
        </ul>
      );
    }
  7. Quickstart: Compile and render MDX content

    main

    To use MDX, follow these three steps:

    1. Install the package.
    2. Transform the content in your collection definition using compileMDX within the transform function. This converts the raw MDX string into a compiled component string.
    3. Render the content in your React application using the MDXContent component from @content-collections/mdx/react.
    import { defineCollection, defineConfig } from "@content-collections/core";
    import { compileMDX } from "@content-collections/mdx";
    import { z } from "zod";
    
    const posts = defineCollection({
      name: "posts",
      directory: "content",
      include: "*.mdx",
      schema: z.object({
        title: z.string(),
        content: z.string(),
      }),
      transform: async (document, context) => {
        const mdx = await compileMDX(context, document);
        return {
          ...document,
          mdx,
        };
      },
    });
    
    export default defineConfig({
      content: [posts],
    });
    import { allPosts } from "content-collections";
    import { MDXContent } from "@content-collections/mdx/react";
    
    export default function App() {
      return (
        <main>
          <h1>Posts</h1>
          <ul>
            {allPosts.map((post) => (
              <li key={post._meta.path}>
                <h2>{post.title}</h2>
                <MDXContent code={post.mdx} />
              </li>
            ))}
          </ul>
        </main>
      );
    }
  8. Configure TypeScript path aliases for Solid Start

    main

    After installing the adapter, you must add a path alias to your tsconfig.json so your application can resolve the generated content collections from the ./.content-collections/generated directory.

    {
      "compilerOptions": {
        // ...
        "paths": {
          "~/*": ["./src/*"],
          "content-collections": ["./.content-collections/generated"]
        }
      }
    }
  9. Transform Markdown to HTML using compileMarkdown

    main

    The @content-collections/markdown package is designed to be used within a Content Collections transform function. It converts plain markdown content into HTML and utilizes the provided context to cache the output, improving build speeds by avoiding unnecessary recompilations.

    To implement this, import compileMarkdown and call it within your collection's transform hook, passing the context and the document.

    import { defineCollection, defineConfig } from "@content-collections/core";
    import { compileMarkdown } from "@content-collections/markdown";
    
    const posts = defineCollection({
      name: "posts",
      directory: "content",
      include: "*.md",
      schema: z.object({
        title: z.string(),
        content: z.string(),
      }),
      transform: async (document, context) => {
        const html = await compileMarkdown(context, document);
        return {
          ...document,
          html,
        };
      },
    });
    
    export default defineConfig({
      content: [posts],
    });
  10. Install the Vite adapter for Content Collections

    main

    To integrate Content Collections into a Vite-based application, follow these three steps:

    1. Install dependencies: Install the core package, the Vite adapter, and zod as dev dependencies.
    2. Configure TypeScript path alias: Add a path alias to your tsconfig.json so you can import your generated collections using the content-collections module name.
    3. Register the plugin: Add contentCollections() to your vite.config.ts plugins array.

    Note on Frameworks: If you are using a framework that has a dedicated Content Collections adapter (e.g., Remix/React Router v7 or SolidStart), use that specific adapter instead of the generic Vite adapter. Use this Vite adapter for frameworks without a dedicated adapter, such as SvelteKit or Qwik.

    npm i @content-collections/core @content-collections/vite zod -D