Collections and schemas

On this page

A collection is a folder of content files that share a schema, a layout and a URL pattern. Every theme comes with two: pages, every page in content/ outside the other collections, and posts, the dated posts in content/posts/ with a list page at /posts/ and a feed. Themes may add their own, and the config adds more or changes the theme's in kilim.config.ts:

import { defineConfig, z } from "@kilim-dev/core";

export default defineConfig({
  theme: "default",
  collections: {
    talks: {
      layout: "post",
      permalink: "/talks/:year/:slug/",
      list: { title: "Talks", pageSize: 10 },
      schema: z.object({
        title: z.string(),
        date: z.coerce.date(),
        description: z.string().optional(),
        draft: z.boolean().optional(),
      }),
    },
  },
});
OptionMeaning
directoryThe folder in content/. Defaults to the collection name; "." is all of content/.
schemaChecks each file's frontmatter. Any Standard Schema works.
layoutThe layout of its pages. A frontmatter layout overrides it. Defaults to page.
permalinkThe URL pattern of its pages; see URLs.
listAdds a page listing the entries, newest date first.
feedWrites RSS next to the list page. On when the collection has a list.
fieldsFrontmatter fields added to the theme's Zod schema, e.g. { mood: z.string().optional() }.

Collection folders can be nested: with pages at "." and posts at posts, a file belongs to the deepest collection whose folder holds it. A theme's collection whose folder the site does not have gets no list page. Without a theme there are no collections until the config declares some, and a file outside all of them gets a warning.

Schemas

z from @kilim-dev/core is Zod. Every file is checked before anything is rendered, and all problems are reported at once, with the file and line:

content/blog/hello.md:3: date: Invalid input: expected date, received Date
content/blog/draft.md:2: title: Invalid input: expected string, received undefined

Layouts and plugins get the validated data, so z.coerce.date() gives them a Date. kilim types writes the collections' types to .kilim/types.d.ts, which types page.data in layouts; kilim dev keeps the file up to date.

Some frontmatter keys mean something to Kilim. Add them to the schema to use them:

KeyEffect
titleThe page title and the default <title>.
dateSorts lists, fills :year, :month and :day in permalinks.
descriptionThe meta description and link previews.
imageThe Open Graph image instead of the generated one.
drafttrue leaves the page out of the build.
publishAtThe page appears from this time on.
expiresAtThe page disappears at this time.
noindexAdds noindex and leaves the page out of the sitemap.
aliasesOld URLs that redirect to the page.
slug, permalinkChange the URL; see URLs.

Drafts and scheduled pages

A page is published when it is not a draft, its publishAt has passed and its expiresAt has not. Times are ISO 8601; without an offset they are in the config's timezone (UTC by default). kilim dev shows unpublished pages anyway, with a badge, so you can read them before they go out.

List pages

list: true or list: { title, pageSize, permalink, layout } adds a page at the collection's URL, e.g. /blog/, with its entries newest first. With pageSize, later pages are at /blog/page/2/ and so on. A collection with a list also gets a feed at /blog/feed.xml (RSS; Atom and JSON Feed are options of the feed() plugin).