Generated pages Some pages don't have a source file of their own: one page per tag, one archive per month. A **page template** generates many pages from one file. ## Bracketed names Write a path segment as `[name]` to make it a parameter. The source is a template if its file name or any folder above it is a parameter: | Source | URL pattern | Example URLs | | --- | --- | --- | | `tags/[tag].jsx` | `/tags/[tag]` | `/tags/go`, `/tags/plan9` | | `logs/[year]/index.jsx` | `/logs/[year]` | `/logs/2025`, `/logs/2026` | | `logs/[year]/[month].jsx` | `/logs/[year]/[month]` | `/logs/2026/11` | | `tags/[tag]/feed.xml.jsx` | `/tags/[tag]/feed.xml` | `/tags/go/feed.xml` | Names are lowercase letters and digits, starting with a letter, and each name appears at most once in a path. The whole segment must be the parameter: `[year]-archive.jsx` is an error, not a page. Templates must be JSX. A Markdown or Go template file inside a bracketed path is an error, and so is a static file or `_`-prefixed file inside a bracketed folder. Put layouts, defaults, and output templates for generated pages in a real folder above the bracketed one (see [Layouts and data](#layouts-and-data-for-generated-pages)). ## Declaring the pages with `params()` A template starts with a `params(...)` call that lists the parameter values to generate. It takes an array of objects, or a function returning one, with exactly the template's parameter names as keys: ```jsx // tags/[tag].jsx params(() => [...new Set(pages("**").flatMap(p => p.tags ?? []))].map(tag => ({ tag }))); data({ title: `Tagged ${page.params.tag}` }); ``` ```jsx // logs/[year]/[month].jsx params(() => pages("logs/**").map(l => ({ year: l.date.slice(0, 4), month: l.date.slice(5, 7), }))); data({ title: `${page.params.year}-${page.params.month}` }); ``` Rules: - `params()` must be among the template's leading statements, before the markup, alongside any `data()` call. Anywhere else it's an error. - Values are strings or numbers (numbers become strings). Each value must be a non-empty single path segment: no `/` or `\`, not `.` or `..`, and not starting with `.` or `_`. - Duplicate sets are merged, so you don't need to deduplicate. An empty array generates no pages. - A set with a missing or extra key, or a bad value, fails the build, naming the template and the set. ## What `params()` can see `params()` runs before any generated page exists, in its own VM with your globals and `pages()`. `page` is undefined there. `pages()` returns only real pages, never generated ones, so one template can't feed another and the set of pages depends only on the files on disk. Once all pages exist, page bodies and layouts see everything: a "Tags" index page can call `pages("tags")` and list every generated tag page. ## Generated page data A generated page's data is built like any page's, plus `params`: | Key | Value for `/logs/2026/11` | | --- | --- | | `params` | `{ year: "2026", month: "11" }` (always strings) | | `path` | `/logs/2026/11` | | `slug` | `11` | | `src` | The template's source | | `isDir` | True if the template is an `index.jsx` | ## Layouts and data for generated pages Globals, defaults, layouts, and output templates are found by the generated page's path, exactly as if a real file lived there. `logs/_layout.jsx` wraps `/logs/2026/11`, and a real `logs/2026/_defaults.jsx` applies to it. HTML generated pages get outputs from `_outputs/` like any page, so `/logs/2026/11.txt` exists if `_outputs/txt.jsx` does. If a generated page's parent folder doesn't exist as a page, taragen adds a grouping node for it, so `pages("logs/*")` reaches years that exist only through generated pages. ## Overriding a generated page A real source at the same path wins, silently. To give one tag a hand-written page with its own intro, add `tags/plan9.md`; every other tag keeps its generated page. A generated page at the path of a grouping node fills it, keeping its subpages: that's how `logs/[year]/index.jsx` gives `/logs/2026` an index. Two templates producing the same path is an error, as is a generated page whose URL matches a static file, output, alias, or redirect. ## Generated pages in listings Generated pages show up in `pages()` results, including `**` patterns. A home stream built from `pages("logs/**")` will include generated month pages; skip them with a filter on `params`: ```jsx pages({ path: ["articles/**", "logs/**"] }).filter(p => !p.params).reverse() ``` ## Drafts and serving `params()` follows the draft rules: in `build` its `pages()` calls never see drafts, so a month that only holds a draft gets no page; in `serve` it does. A generated page whose own data makes it a draft is skipped by `build`. In `serve`, a URL that fits the pattern but wasn't generated, like `/tags/nosuchtag`, isn't a page. Adding a tag to a Markdown file creates its page on the next reload.