Skip to content

Upgrade Guide (v0.x -> v1.0)

ContentKit v1.0 introduces a new, more flexible configuration API. While the legacy configuration format is still supported for now, we strongly recommend upgrading to the new format to take advantage of improved type safety and developer experience.

Migration Steps

1. Update Imports

Replace the old ContentKitConfig type import with the new helper functions.

Old:

ts
import type { ContentKitConfig } from "contentkit/types";

const config: ContentKitConfig = {
  /* ... */
};
export default config;

New:

ts
import { defineConfig, defineCollection, fields } from "contentkit";

// ... collections definition

export default defineConfig({
  collections: [
    /* ... */
  ],
});

2. Define Collections

Instead of a single documentTypes array, define each collection individually using defineCollection.

Old:

ts
documentTypes: [
  {
    name: "Post",
    filePathPattern: "posts/**/*.md",
    fields: {
      /* ... */
    },
  },
];

New:

ts
const posts = defineCollection({
  name: "Post",
  directory: "./content/posts", // Base directory for this collection
  include: "**/*.md", // Glob pattern relative to directory
  schema: {
    /* ... */
  },
});

3. Update Field Definitions

Use the fields helper to define your schema. This provides better type inference and autocomplete.

Old:

ts
fields: {
  title: { type: "string", required: true },
  tags: { type: "array", items: { type: "string" } },
  author: {
    type: "object",
    required: true,
    fields: {
      name: { type: "string", required: true }
    }
  }
}

New:

ts
schema: {
  title: fields.string(),
  tags: fields.array(fields.string()).optional(), // or fields.list(...)
  author: fields.object({
    name: fields.string(),
  }),
}

Note: Fields are required by default. Use .optional() to make them optional.

4. Update Computed Fields

Computed fields are now defined using the .resolve() method on a field definition.

Old:

ts
computedFields: {
  slug: {
    type: "string",
    resolve: (doc) => doc.title.toLowerCase().replace(/\s+/g, "-"),
  },
}

New:

ts
computedFields: {
  slug: fields.string().resolve((doc) => doc.title.toLowerCase().replace(/\s+/g, "-")),
}

5. Remove Global Options

outputFormat and generateTypes are now automatically detected from your project's package.json and tsconfig.json. You can remove them from your config unless you need to override the defaults.

Old:

ts
export default {
  contentDirPath: "content", // Now handled per-collection via `directory`
  outputFormat: "esm", // Auto-detected
  generateTypes: true, // Auto-detected
  // ...
};

New:

ts
export default defineConfig({
  collections: [posts],
});

Full Example

Before:

ts
import type { ContentKitConfig } from "contentkit/types";

const config: ContentKitConfig = {
  contentDirPath: "content",
  outputFormat: "esm",
  generateTypes: true,
  documentTypes: [
    {
      name: "Post",
      filePathPattern: "posts/**/*.md",
      fields: {
        title: { type: "string", required: true },
        date: { type: "date", required: true },
      },
      computedFields: {
        slug: {
          type: "string",
          resolve: (doc) => doc._raw.sourceFileName.replace(/\.md$/, ""),
        },
      },
    },
  ],
};

export default config;

After:

ts
import { defineConfig, defineCollection, fields } from "contentkit";

const posts = defineCollection({
  name: "Post",
  directory: "./content/posts",
  include: "**/*.md",
  schema: {
    title: fields.string(),
    date: fields.date(),
  },
  computedFields: {
    slug: fields
      .string()
      .resolve((doc) => doc._raw.sourceFileName.replace(/\.md$/, "")),
  },
});

export default defineConfig({
  collections: [posts],
});

Released under the BSD-3-Clause License.