Defining a collection
A collection is a group of pages sharing a sort order, a permalink shape and a default template.
content {
collections {
posts "posts/**/*.typ" {
sort "date"
reverse #true
permalink "/posts/{slug}/"
template "post.typ"
}
}
}
The node name is the collection id. The leading string is a glob selecting its members, and the block tunes the rest.
You don’t have to declare anything: a file in a subdirectory of content/ joins a collection named after that top directory, with every default below. Declaring posts { } only changes what you name.
Keys
| Key | Type | Default | Does |
|---|---|---|---|
glob |
str | the directory of the same name | Which content files belong to the collection. |
sort |
order | date | title |
order |
What its members are ordered by. |
reverse |
bool | #false |
Reverse that order. |
permalink |
template | /{path}/{slug}/ |
The URL pattern its pages publish at. |
template |
str | – | The layout its pages render through. |
paginate |
block | – | Generate an index over the collection. |
feed |
bool | #false |
Also write a feed of its members, beside that index. |
schema |
block | – | What every member’s frontmatter must declare. |
paginate is on listings and pagination feed is on feeds schema is on frontmatter.
Which files belong
The glob is matched against paths relative to content/, so it can reach anywhere:
content {
collections {
posts { glob "blog/**/*.typ" }
notes "til/*.typ"
}
}
Glob-configured collections claim their files first; whatever is left falls back to the convention. A file directly under content/ that no glob claims joins _root: it publishes at /{slug}/, or at / if it is the index.
_root is a collection like any other, so configuring it is how the home page and its neighbours get a layout without each naming one:
content {
collections {
_root { template "page.typ" }
}
}
A theme states that in its own theme.kdl, which is what lets a themed page render with nothing bound to it by hand.
Order
sort picks the key, reverse flips it. Pages that share a key, or lack one entirely, fall back to source path, so the order is stable across machines.
content {
collections {
guide { sort "order" }
blog { sort "date"; reverse #true }
}
}
This is the order that drives the prev/next pager, the generated index pages, and the section tree a sidebar is built from. Change it in one place and all three move together.
Permalinks
permalink is a template of literal text and {placeholder} tokens.
| Token | Fills with |
|---|---|
{slug} |
The page’s slug. |
{path} |
Where the page sits under content/, however deep. Several segments at once. |
{collection} |
The collection’s id, and nothing else. |
{year}, {month}, {day} |
The page’s date, zero-padded. Empty when it has none. |
{order} |
The page’s order. Empty when it has none. |
content {
collections {
posts { permalink "/{year}/{month}/{slug}/" }
}
}
{path} and {collection} are the same string for the ordinary layout, since a collection is a directory. They part company once you nest: with the default template, content/ship/deploy/s3.typ publishes at /ship/deploy/s3/, while /{collection}/{slug}/ would flatten it to /ship/s3/.
NOTE
A placeholder with nothing to render drops its segment rather than leaving an empty one, so a dateless page under/{year}/{slug}/ lands at /hello/ rather than //hello/. An unknown token is a config error with a span, not a silent fallback.
Templates
template names the layout every member renders through, relative to paths { templates }. A page overrides it with template in its own frontmatter, and a page whose collection names none is written out unwrapped. What the layout receives is on templates.