Skip to content

Define Collections

Content collections are the best way to manage and author content in content-first applications. Velite helps you organize and validate your contents, and provides type-safety through automatic type generations.

What is a Collection?

A content collection is a group of related content items. For example, a blog might have a collection of posts, a collection of authors, and a collection of tags.

Each collection should be placed in a top-level directory inside the content project directory.

diff
content
├── authors # => authors collection
│   ├── zce.yml
│   └── jane.yml
├── posts # => posts collection
│   ├── hello-world.md
│   └── another-post.md
└── tags # => tags collection
    └── all-in-one.yml

Each collection is defined by a schema, which describes the shape of the content items in the collection.

js
import { defineCollection, defineConfig, s } from 'velite'

const posts = defineCollection({
  /* collection shema options */
})

const authors = defineCollection({
  /* collection shema options */
})

const tags = defineCollection({
  /* collection shema options */
})

export default defineConfig({
  collections: { authors, posts, tags }
})

Collection Schema Options

name

The name of the collection. This is used to generate the type name for the collection.

js
const posts = defineCollection({
  name: 'Post'
})

The type name is usually a singular noun, but it can be any valid TypeScript identifier.

pattern

The glob pattern used to find content files for the collection.

js
const posts = defineCollection({
  pattern: 'posts/**/*.md'
  // or pattern: ['posts/**/*.md', '!posts/private/**']
})

Velite uses fast-glob to find content files, so you can use any glob pattern supported by fast-glob.

By default, Velite will ignore files and directories that start with _ or ..

single

Whether the collection should be treated as a single item. This is useful for collections that only have one content item, such as a site's metadata.

js
const site = defineCollection({
  pattern: 'site/index.yml',
  single: true
})

schema

Velite uses Zod to validate the content items in a collection. The schema option is used to define the Zod schema used to validate the content items in the collection.

To use Zod in Velite, import the z utility from 'velite'. This is a re-export of Zod's z object, and it supports all of the features of Zod. See Zod's Docs for a complete documentation on how Zod works and what features are available.

js
import { z } from 'velite'

const posts = defineCollection({
  schema: z.object({
    title: z.string().max(99)
  })
})

TIP

The schema is usually a ZodObject, validating the shape of the content item. But it can be any valid Zod schema.

For more complex schemas, I recommend that you use Velite extended schemas s:

  • s.slug(): validate slug format, unique in posts collection.
  • s.isodate(): format date string to ISO date string.
  • s.unique(): validate unique value in collection.
  • s.image(): input image relpath, output image object with blurImage.
  • s.file(): input file relpath, output file public path.
  • s.metadata(): extract markdown reading-time, word-count, etc.
  • s.excerpt(): excerpt of markdown content
  • s.markdown(): transform markdown to html
  • s.mdx(): transform mdx to function code.

For example:

js
import { s } from 'velite'

const posts = defineCollection({
  schema: s.object({
    slug: s.slug('posts'),
    date: s.isodate(),
    cover: s.image(),
    video: s.file().optional(),
    metadata: s.metadata(),
    excerpt: s.excerpt(),
    content: s.markdown()
  })
})

For more information about Velite extended field schema, see Velite Schemas.

Schema Transform (Computed Fields)

Zod schemas can be transformed using the .transform() method. This is useful for adding computed fields to the content items in a collection.

js
const posts = defineCollection({
  schema: s
    .object({
      slug: s.slug('posts')
    })
    .transform(data => ({
      ...data,
      // computed fields
      permalink: `/blog/${data.slug}`
    }))
})

Transform Context Metadata

The transform() function can receive a second argument, which is the context object. This is useful for adding computed fields to the content items in a collection.

js
const posts = defineCollection({
  schema: s
    .object({
      // fields
    })
    .transform((data, { meta }) => ({
      ...data,
      // computed fields
      path: meta.path // or parse to filename based slug
    }))
})

the type of meta is ZodMeta, which extends VeliteFile. for more information, see Custom Schema.

Content Body

Velite's built-in loader keeps content's raw body in meta.content, and the plain text body in meta.plain.

To add them as a field, you can use a custom schema.

js
const posts = defineCollection({
  schema: s.object({
    content: s.custom().transform((data, { meta }) => meta.content),
    plain: s.custom().transform((data, { meta }) => meta.plain)
  })
})

TIP

This will keep the original document body, In most cases, you should use s.markdown() / s.mdx() schema to transform the document body.

Markdown & MDX

In addition to validating the content items in a collection, Velite can also process the content body using Unified.

js
const posts = defineCollection({
  schema: s.object({
    content: s.markdown() // or s.mdx()
  })
})

The content field will be transformed from markdown to html, and the result will be available in the content field of the content item.

Reference

Metadata

Velite can extract metadata from content files. This is useful for adding computed fields to the content items in a collection.

js
const posts = defineCollection({
  schema: s.object({
    metadata: s.metadata() // extract markdown reading-time, word-count, etc.
  })
})

Reference

Excerpt

Velite can extract excerpt from content files. This is useful for adding computed fields to the content items in a collection.

js
const posts = defineCollection({
  schema: s.object({
    excerpt: s.excerpt({ length: 200 }) // excerpt of the markdown body
  })
})

Reference

Distributed under the MIT License.