Beyond Blog Posts: Structuring Content with Jekyll Collections
A practical guide to Jekyll collections: defining custom content types, rendering them in Liquid, and applying front-matter defaults without build-time surprises.
18 Mar 2026, 17:43 UTC

You have a portfolio site. You have team member bios. You have product documentation. None of these are blog posts, yet Jekyll's default content model pushes everything into _posts with date-based filenames. Forcing structured content into a chronological stream creates messy URLs, awkward sorting, and front matter that lies about what the content actually is.
Jekyll collections solve this by letting you define custom content types with their own folders, URL patterns, and metadata rules. This article walks through defining a collection, rendering it in templates, and configuring defaults—plus the performance trade-offs you should understand before scaling up.
Defining a collection in _config.yml
A collection is a named folder in your source tree that Jekyll treats as a distinct content type. You define it in _config.yml under the collections key. Each collection can specify whether it outputs HTML pages and what permalink structure to use.
Here is a minimal books collection:
# _config.yml
collections:
books:
output: true
permalink: /books/:title/
With this configuration, Jekyll expects Markdown files in a top-level _books directory. The output: true flag tells Jekyll to render each file as a standalone page. The permalink option overrides the default URL structure, using :title as a placeholder for the filename slug.
Create a file at _books/the-pragmatic-programmer.md:
---
title: "The Pragmatic Programmer"
author: "Andrew Hunt"
release_date: 1999-10-20
layout: book
---
Your journey to mastery.
Run the build from your project root:
jekyll build
After the build completes, check the _site directory. You should find a generated page at _site/books/the-pragmatic-programmer/index.html (or .html, depending on your permalink style). If the folder does not appear, verify that output: true is set and that the collection folder name matches the key in _config.yml.
Rendering collection items in templates
Collections expose their documents through the site variable in Liquid. For the books collection defined above, site.books returns an array of documents that you can loop over, sort, and filter.
A typical index page might live at books.html in your root directory:
---
layout: default
title: Books
permalink: /books/
---
<h1>Reading List</h1>
<ul>
{% assign sorted_books = site.books | sort: "release_date" %}
{% for book in sorted_books %}
<li>
<a href="{{ book.url }}">{{ book.title }}</a>
by {{ book.author }}
</li>
{% endfor %}
</ul>
Because collections are resolved at generation time, filtering happens during the build rather than at request time. This keeps rendered pages fast, but it also means every template loop adds work to the build process.
Front-matter defaults and consistent metadata
Typing the same layout or author into every file gets old. Jekyll's front-matter defaults let you set shared values for an entire collection without touching individual documents. Defaults are configured under the defaults key in _config.yml.
# _config.yml
defaults:
- scope:
path: ""
type: books
values:
layout: book
author: "Unknown"
This configuration applies the book layout and an author fallback to every document in the books collection. If a document specifies its own author in front matter, that value wins. This reduces duplication and prevents pages from rendering with missing metadata.
One caution: naming a collection posts overrides Jekyll's built-in behavior. That can break features like the RSS feed and pagination that assume the standard post structure. Choose a collection name that does not collide with Jekyll's reserved names.
The trade-off: build time and incremental regeneration
Collections are powerful, but they are not free. Every document in a collection is parsed and rendered during jekyll build. For small collections—dozens or even hundreds of items—the overhead is negligible. For thousands of documents, build times can climb, especially if you use --incremental.
Incremental builds only regenerate files that changed. That sounds ideal, but Jekyll's incremental regeneration tracks dependencies conservatively. If you change a layout, a default, or a global config value, Jekyll may rebuild large portions of the site anyway. For large collections, consider whether you truly need every item as a standalone output page, or whether an index page with inline content would suffice.
Verifying the result
To confirm your collection is working:
- Run
jekyll --versionto verify you are on Jekyll 4.x or later, which fully supports the collection behavior described here. - Run
jekyll buildfrom your project root. - Check that
_site/books/exists and contains a subdirectory or HTML file matching your permalink pattern. - Open the generated HTML and confirm that front-matter defaults were applied—if you set
layout: bookin defaults but omitted it in a document, the generated page should still use that layout. - If the output looks wrong, run
jekyll build --verboseto see which files are being read and written.
If you need to revert, remove the collections and defaults blocks from _config.yml and delete the _books folder from your source tree. The generated _site folder can be safely deleted and rebuilt.
Collections turn Jekyll from a blogging engine into a general-purpose static site generator. Start with one collection, verify the output, and add more only when the content genuinely demands a separate type.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.