Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Matej Bačo (Meldiron) - Contributor
Simon Binder (simolus3) - Contributor
Mahdi Khodadadifard (xclud) - Contributor
Jamiu Okanlawon (developerjamiu) - Contributor
Jan Bittner (tenhobi) - Contributor
13 changes: 13 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,19 @@
{
"title": "Secondary Outputs",
"href": "/content/concepts/secondary_outputs"
},
{
"group": "Pages Aggregator",
"pages": [
{
"title": "Overview",
"href": "/content/pages_aggregator"
},
{
"title": "Taxonomy",
"href": "/content/pages_aggregator/taxonomy"
}
]
}
]
},
Expand Down
16 changes: 16 additions & 0 deletions docs/content/concepts/route_loading.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ ContentApp.custom(
);
```

### TaxonomyLoader

The `TaxonomyLoader` generates pages from taxonomy terms (like tags or categories) extracted from content frontmatter.

- **Source**: Frontmatter fields in content files.
- **Behavior**: Scans a directory for content files, extracts unique values from a specified frontmatter field, and generates a page for each term.
- **Use Cases**:
- Tag or category archive pages for a blog.
- Any classification-based page generation.
- **Features**:
- Automatic term extraction and normalization.
- Optional taxonomy index page.
- Context extensions for querying taxonomy data.

[Learn more about Taxonomy](/content/pages_aggregator/taxonomy).

## Creating Custom Route Loaders

If the built-in loaders don't fit your needs, you can create a custom `RouteLoader` implementation. At minimum, you'll need to:
Expand Down
87 changes: 87 additions & 0 deletions docs/content/pages_aggregator/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: Pages Aggregator
description: Generate additional routes by analyzing all loaded pages.
---

---

A `PagesAggregator` generates additional routes after all pages are loaded. Unlike route loaders, aggregators don't load pages from a source — they analyze the already-loaded pages and produce routes based on their content or metadata.

This is useful for any case where the set of routes depends on the content of your pages, such as tag pages, category archives, or any other derived view.

---

## How It Works

Aggregators run after all `RouteLoader`s have finished loading content pages. Each aggregator receives the full list of loaded content pages and returns a list of `Route`s to add to the router.

```dart
ContentApp.custom(
eagerlyLoadAllPages: true,
loaders: [FilesystemLoader('content')],
pagesAggregators: [
MyAggregator(),
],
configResolver: PageConfig.all(/* ... */),
);
```

<Info>
**Eager loading** must be enabled (`eagerlyLoadAllPages: true`) when using aggregators, as they depend on all pages being loaded before generating routes.
</Info>

## Route Info

Alongside routes, aggregators can produce `AggregatedRouteInfo` objects that carry metadata for each generated route. These are accessible from within a route builder via `context.currentRouteInfo<T>()`:

```dart
final info = context.currentRouteInfo<MyRouteInfo>();
```

This lets you pass structured data from the aggregator to the route builder without relying on closures or global state.

## Built-in Aggregator: TaxonomyAggregator

`jaspr_content` ships with a built-in aggregator for taxonomy use cases — `TaxonomyAggregator`. It scans page frontmatter for a given field (e.g., `tags`) and generates routes for each unique value (term):

```dart
TaxonomyAggregator(
taxonomy: 'tags',
termPageBuilder: (context, taxonomy, term) => TagPage(term: term),
taxonomyPageBuilder: (context, taxonomy) => TagsPage(),
)
```

See the [Taxonomy](/content/pages_aggregator/taxonomy) page for details.

## Custom Aggregators

To create a custom aggregator, extend `PagesAggregator` and implement `aggregatePages`:

```dart
class ArchiveAggregator extends PagesAggregator {
@override
Future<List<Route>> aggregatePages(
List<Page> pages,
List<AggregatedRouteInfo> routeInfos,
) async {
// Group pages by year based on a 'date' frontmatter field.
final byYear = <int, List<Page>>{};
for (final page in pages) {
final date = page.data.page['date'];
if (date is String) {
final year = DateTime.parse(date).year;
byYear.putIfAbsent(year, () => []).add(page);
}
}

return [
for (final entry in byYear.entries)
Route(
path: '/archive/${entry.key}',
builder: (context, _) => ArchivePage(year: entry.key, pages: entry.value),
),
];
}
}
```
Loading
Loading