Listing pages with pages()
pages() returns page data objects, which is how you build indexes, archives,
feeds, and tag pages. Each result has the page's full data, including path, title, date, body, outputs, links, backlinks, and a recursive subpages array.
By path
A plain path returns the children of that page:
pages("docs") // pages directly inside docs/
By pattern
A path containing wildcards returns the children of every page whose path matches:
| Pattern | Returns |
|---|---|
articles/* |
Pages two levels below articles (children of each articles/<x>), e.g. posts in year folders. |
logs/202? |
Children of logs/2020 through logs/2029. |
articles/** |
Every page below articles, at any depth. |
** |
Every page in the site except the root. |
*, ?, and [...] match within one path segment and never cross a /. A
segment that is exactly ** matches zero or more whole segments. (a** inside
a longer segment is just two *s.)
Patterns containing ** leave out grouping nodes (folders with no index file),
since those only exist to hold the tree together. Plain paths, single-level
patterns, and subpages still include them.
Each page appears at most once in a result.
The object form: several paths and filters
pages() also takes an object:
pages({ path: ["articles/**", "logs/**", "docs/**"] }) // a mixed stream
pages({ where: { tags: "plan9" } }) // everything tagged plan9
pages({ path: "articles/**", where: { featured: true } })
| Key | Default | Meaning |
|---|---|---|
path |
"**" |
A path or pattern as in the string form, or a list of them. A list returns the union, with no duplicates, sorted as one listing. |
where |
none | Page data keys and the values to keep. A page must match every key. |
A where key matches when:
-
the wanted value is a string, number, or boolean, and the page's value is
equal to it, or is a list containing it (so
tags: "plan9"matchestags: [go, plan9]); -
or the wanted value is a list, and any of its elements matches by the rule
above (so
tags: ["go", "rust"]means "tagged go or rust").
Comparison is strict: "2026" doesn't match 2026, and strings are
case-sensitive. A page without the key never matches. where filters only the
top-level results, not their subpages.
pages("x") and pages({ path: "x" }) return the same pages. Unknown keys, a path that isn't a string or list of strings, a where value that is an object
or null, and malformed patterns throw an error that fails the render.
Order
Every listing (from either form, and every subpages array) is sorted the same
way:
- Pages without a valid
datecome first, then dated pages, oldest first. - Pages with the same date, or both undated, are ordered by
slug. - Pages with the same slug are ordered by
path.
So listings are stable from build to build. Call .reverse() for newest first,
which puts undated pages at the end:
{pages("articles/**").reverse().slice(0, 10).map(a => partial("_partials/card", a))}
What's in a listing
-
In
build, drafts are left out of every result. Inserve, they're included. -
Generated pages are included, and are recognizable by
their
paramskey. To leave them out:.filter(p => !p.params). - Grouping nodes are included except in
**patterns, as described above.