Collections
Collections are groups of content entries that share a structure. A blog, a set of pages, a product catalog — each is a collection. Every collection is backed by a blueprint that defines its fields, and entries are stored as flat Markdown files with YAML frontmatter.
Collections are the primary way content is organised in Madori. Each collection appears in the Control Panel sidebar and generates REST and GraphQL API endpoints automatically.
Configuration Reference
Collection Definition
Collection definitions live at resources/collections/{handle}.yaml. The handle becomes the collection's identifier throughout the system.
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
title |
string |
Yes | — | Display name shown in the Control Panel sidebar |
blueprint |
string |
Yes | — | Handle of the blueprint that defines the field schema |
blueprints |
string[] |
No | — | Array of blueprint handles (for multi-type collections) |
route |
string |
No | — | Frontend URL pattern for entries (e.g. /blog/{slug}) |
sortable |
boolean |
No | false |
Allow manual drag-and-drop ordering of entries |
dated |
boolean |
No | false |
Use date-based file organisation |
defaultStatus |
string |
No | draft |
Default status for new entries: published or draft |
icon |
string |
No | — | Lucide icon name for the Control Panel sidebar |
sortDirection |
string |
No | desc |
Default sort direction: asc or desc |
taxonomies |
string[] |
No | [] |
Array of taxonomy handles to associate with this collection |
Entry Storage
Entries are stored as Markdown files at content/collections/{collection}/{slug}.md. The YAML frontmatter holds metadata and custom field values; the body is the content field.
# Frontmatter fields (auto-managed)
title: string # Entry title
slug: string # URL-safe identifier (filename)
status: string # "published" or "draft"
createdAt: string # ISO 8601 timestamp
updatedAt: string # ISO 8601 timestamp
Route Patterns
The route option supports these placeholders:
| Placeholder | Description | Example |
|---|---|---|
{slug} |
Entry slug | /blog/hello-world |
{year} |
Year from createdAt | /blog/2026/hello-world |
{month} |
Month from createdAt | /blog/2026/01/hello-world |
{day} |
Day from createdAt | /blog/2026/01/15/hello-world |
Related Configuration
Collections reference these other configuration files:
| Resource | Path | Description |
|---|---|---|
| Blueprint | resources/blueprints/collections/{handle}.yaml |
Field schema |
| Taxonomy | resources/taxonomies/{handle}.yaml |
Category/tag definitions |
| Content | content/collections/{handle}/ |
Entry files |
Usage Examples
Basic Blog Collection
Definition (resources/collections/blog.yaml):
title: Blog
blueprint: blog
route: /blog/{slug}
defaultStatus: draft
icon: pen-line
sortDirection: desc
taxonomies:
- tags
- categories
Creating Entries via the Control Panel
- Navigate to your collection in the CP sidebar
- Click Create Entry
- Fill in the fields defined by the blueprint
- Click Save
The entry is stored immediately as a flat file.
Creating Entries via Files
Create a Markdown file at content/collections/{collection}/{slug}.md:
---
title: Hello World
slug: hello-world
status: published
createdAt: 2026-01-01T00:00:00.000Z
updatedAt: 2026-01-01T00:00:00.000Z
tags:
- javascript
- tutorials
---
Your markdown content here.
Querying via GraphQL
{
blogs(limit: 10, sort: "createdAt:desc") {
title
slug
content
createdAt
}
}
Querying via REST API
GET /api/entries/{collection} # List entries
GET /api/entries/{collection}/{slug} # Get single entry
POST /api/entries/{collection} # Create entry
PUT /api/entries/{collection}/{slug} # Update entry
DELETE /api/entries/{collection}/{slug} # Delete entry
Filtering and Sorting
GraphQL list queries accept these arguments:
| Argument | Type | Description |
|---|---|---|
filter |
object |
Key-value object matching field values |
limit |
number |
Maximum entries to return |
offset |
number |
Skip N entries (for pagination) |
sort |
string |
"fieldName:asc" or "fieldName:desc" |
{
blogs(
filter: { status: "published", author: "admin" }
limit: 5
offset: 0
sort: "createdAt:desc"
) {
title
slug
}
}
Common Patterns
Multi-Blueprint Collections
A collection can support multiple entry types by referencing multiple blueprints:
# resources/collections/pages.yaml
title: Pages
blueprints:
- page
- landing-page
- documentation
route: /{slug}
When creating a new entry, the editor chooses which blueprint to use.
Date-Based Organisation
For collections with time-sensitive content:
title: Blog
blueprint: blog
route: /blog/{year}/{month}/{slug}
dated: true
sortDirection: desc
defaultStatus: draft
Manual Ordering
For collections where order matters (team members, portfolio items):
title: Team
blueprint: team-member
sortable: true
sortDirection: asc
Editors can drag and drop entries in the list view to set their display order.
Taxonomy Integration
Associate taxonomies with a collection to enable categorisation:
# resources/collections/blog.yaml
title: Blog
blueprint: blog
taxonomies:
- categories
- tags
Then include taxonomy fields in your blueprint:
- handle: categories
field:
type: taxonomy
display: Categories
required: true
- handle: tags
field:
type: taxonomy
display: Tags
Content in Version Control
Since entries are flat Markdown files, commit them to Git for:
- Version history of all content changes
- Pull request workflows for content review
- Easy rollback if something goes wrong
- Consistent content across environments (dev/staging/production)
git add content/collections/blog/new-post.md
git commit -m "Add blog post: Getting Started with Madori"