Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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 @@ -16,3 +16,4 @@ Simon Lightfoot (slightfoot) - Contributor
Matej Bačo (Meldiron) - Contributor
Simon Binder (simolus3) - Contributor
Mahdi Khodadadifard (xclud) - Contributor
Jan Bittner (tenhobi) - Contributor
17 changes: 17 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,23 @@
{
"title": "Secondary Outputs",
"href": "/content/concepts/secondary_outputs"
},
{
"group": "Taxonomy",
"pages": [
{
"title": "Overview",
"href": "/content/concepts/taxonomy"
},
{
"title": "Usage",
"href": "/content/taxonomy/usage"
},
{
"title": "TaxonomyLoader",
"href": "/content/taxonomy/taxonomy_loader"
}
]
}
]
},
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 @@ -126,6 +126,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/concepts/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
83 changes: 83 additions & 0 deletions docs/content/concepts/taxonomy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Taxonomy
description: Classify content with taxonomies like tags and categories.
---

---

Taxonomies let you classify content using frontmatter fields like **tags** or **categories**. A taxonomy is a classification system where each unique value is called a **term**. For example, a "tags" taxonomy might have the terms "dart", "flutter", and "jaspr".

The `TaxonomyLoader` scans your content files, extracts all unique terms from a frontmatter field, and generates pages for each one. Combined with the `TaxonomyContext` extension, you can query taxonomy data from within component builders.

---

## How It Works

Given blog posts with a `tags` field in their frontmatter:

```markdown title="content/posts/hello-world.md"
---
title: Hello World
tags: [Dart, Flutter]
---

Welcome to my blog!
```

```markdown title="content/posts/building-with-jaspr.md"
---
title: Building with Jaspr
tags: [Dart, Jaspr]
---

Let's build something!
```

The `TaxonomyLoader` will:

1. Scan the content directory for files with frontmatter.
2. Extract unique values from the `tags` field: "Dart", "Flutter", "Jaspr".
3. Normalize them to lowercase with dashes: `dart`, `flutter`, `jaspr`.
4. Generate a page for each term at `/tags/dart`, `/tags/flutter`, `/tags/jaspr`.
5. Optionally generate a taxonomy index page at `/tags/`.

Each generated page carries metadata identifying its taxonomy and term, which the context extensions use for querying.

## Term Normalization

All terms are normalized before use. The normalization converts values to lowercase and replaces spaces with dashes:

- `"Dart"` → `dart`
- `"My Tag"` → `my-tag`
- `"Hello World"` → `hello-world`

This means `tags: [Dart]` and `tags: [dart]` in different files refer to the same term.

## Multiple Taxonomies

You can use multiple `TaxonomyLoader`s for different classification systems:

```dart
ContentApp.custom(
eagerlyLoadAllPages: true,
loaders: [
FilesystemLoader('content'),
TaxonomyLoader(
taxonomy: 'tags',
termPageBuilder: (context, taxonomy, term) { /* ... */ },
),
TaxonomyLoader(
taxonomy: 'categories',
termPageBuilder: (context, taxonomy, term) { /* ... */ },
),
],
// ...
);
```

Each taxonomy operates independently — terms from one taxonomy do not interfere with another.

## Next Steps

- [**Usage**](/content/taxonomy/usage): Learn how to set up taxonomy and query taxonomy data from components.
- [**TaxonomyLoader**](/content/taxonomy/taxonomy_loader): Detailed reference for all TaxonomyLoader properties and options.
125 changes: 125 additions & 0 deletions docs/content/taxonomy/taxonomy_loader.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
title: TaxonomyLoader
description: Reference for the TaxonomyLoader route loader and its properties.
---

---

The `TaxonomyLoader` is a `RouteLoader` that scans content files for a frontmatter field matching the given taxonomy name, extracts all unique values (terms), normalizes them, and generates in-memory pages for each one.

For a general introduction to taxonomies, see the [Taxonomy overview](/content/concepts/taxonomy). For setup and querying, see [Using Taxonomy](/content/taxonomy/usage).

---

## Properties

<Card title="Properties" icon="sliders">
<Property name="taxonomy" type="String" required>
The frontmatter key to extract terms from (e.g., `'tags'`, `'categories'`).
</Property>

---

<Property name="termPageBuilder" type="TaxonomyTermPageBuilder" required>
Builder function for individual term pages. Receives the `BuildContext`, the taxonomy name, and the normalized term string.

```dart
termPageBuilder: (context, taxonomy, term) => TagPage(term: term),
```
</Property>

---

<Property name="directory" type="String">
The directory to scan for content files. Defaults to `'content'`.
</Property>

---

<Property name="taxonomyPageBuilder" type="TaxonomyPageBuilder?">
Optional builder for the taxonomy index page. Receives the `BuildContext` and the taxonomy name. If not provided, no index page is generated.

```dart
taxonomyPageBuilder: (context, taxonomy) => TagsPage(),
```
</Property>

---

<Property name="taxonomySlug" type="String?">
The URL slug for the taxonomy index page. Defaults to the taxonomy name.
For example, with `taxonomy: 'tags'` and `taxonomySlug: 'labels'`, the index page is generated at `/labels/` instead of `/tags/`.
</Property>

---

<Property name="termSlug" type="String?">
The URL slug for individual term pages. Defaults to the taxonomy name.
For example, with `taxonomy: 'tags'` and `termSlug: 'tag'`, term pages are generated at `/tag/dart` instead of `/tags/dart`.
</Property>

---

<Property name="initialTermDataBuilder" type="TaxonomyTermInitialDataBuilder?">
Optional function to provide initial data for each term page. Receives the taxonomy name and the normalized term string. The returned map is merged with the taxonomy metadata under the `page` namespace.

```dart
initialTermDataBuilder: (taxonomy, term) => {
'page': {'title': 'Posts tagged: $term'},
},
```
</Property>

---

<Property name="taxonomyInitialDataBuilder" type="TaxonomyInitialDataBuilder?">
Optional function to provide initial data for the taxonomy index page. Receives the taxonomy name.

```dart
taxonomyInitialDataBuilder: (taxonomy) => {
'page': {'title': 'All $taxonomy'},
},
```
</Property>

---

<Property name="supportedExtensions" type="List&lt;String&gt;">
File extensions to scan for frontmatter. Defaults to `['.md', '.html']`.
</Property>
</Card>

## Initial Data

You can provide initial data for generated pages using the `initialTermDataBuilder` and `taxonomyInitialDataBuilder` callbacks. This data is merged with the taxonomy metadata that `TaxonomyLoader` injects automatically — the taxonomy metadata always takes precedence.

```dart
TaxonomyLoader(
taxonomy: 'tags',
// Data for each term page.
initialTermDataBuilder: (taxonomy, term) => {
'page': {'title': 'Posts tagged: $term', 'layout': 'archive'},
},
// Data for the index page.
taxonomyInitialDataBuilder: (taxonomy) => {
'page': {'title': 'All $taxonomy'},
},
termPageBuilder: (context, taxonomy, term) => TagPage(term: term),
taxonomyPageBuilder: (context, taxonomy) => TagsPage(),
);
```

This data is accessible via `context.page.data` in layouts and templates, just like frontmatter data from content files.

## URL Customization

By default, generated pages use the taxonomy name as the URL prefix. Use `taxonomySlug` and `termSlug` to customize the URL structure:

```dart
TaxonomyLoader(
taxonomy: 'tags',
taxonomySlug: 'labels', // Index page at /labels/ instead of /tags/
termSlug: 'label', // Term pages at /label/dart instead of /tags/dart
// ...
)
```
Loading
Loading