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: ```jsx 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/`), 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: ```jsx 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"` matches `tags: [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: 1. Pages without a valid `date` come first, then dated pages, oldest first. 2. Pages with the same date, or both undated, are ordered by `slug`. 3. 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: ```jsx {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. In `serve`, they're included. - [Generated pages](/docs/manual/generated-pages) are included, and are recognizable by their `params` key. To leave them out: `.filter(p => !p.params)`. - Grouping nodes are included except in `**` patterns, as described above.