Documentation
Cross-linking pages with all_pages
Every template in markdown-mode sites has access to a global all_pages variable — a list of every .md page in the site with its full frontmatter. This lets any layout pull in and filter content from other sections without any special configuration.
How it works
Every page summary in all_pages carries these fields:
| Field | Example | Description |
|---|---|---|
title | "Alice Müller" | From frontmatter title: |
url | "/users/alice" | Absolute URL of the page |
slug | "alice" | Last path segment |
section | "/users" | Parent path |
extra | {location: "lake-constance"} | All other frontmatter fields |
extra_lists | {hosts: ["alice","bob"]} | List-valued frontmatter fields |
tags | ["guide","beginner"] | Tag list |
date | "2026-06-15" | Frontmatter date |
snippet | "A gentle paddle…" | First ~200 chars of body |
You filter all_pages in templates using Tera's built-in filter filter with attribute paths:
{% set guides = all_pages | filter(attribute="section", value="/users") %}
{% set here = all_pages | filter(attribute="extra.location", value="lake-constance") %}
Tutorial: users, activities, and locations
This example builds a small site with three entity types that link to each other.
Content files
users/alice.md
activities/morning-kayak.md
locations/lake-constance.md
magic-circle.md — a standalone view page with no body:
Layouts
Create these files under layouts/ in your site's git repository.
layouts/user-profile.html — resolves the user's location, lists their activities:
{% extends "base.html" %}
{% block main %}
{{ page.title }}
{{ page.extra.role }}
{{ page.content | safe }}
{% set location = all_pages | filter(attribute="slug", value=page.extra.location) | first %}
{% if location %}
Based at {{ location.title }}
{% endif %}
{% set activities = all_pages | filter(attribute="section", value="/activities")
| filter(attribute="extra.host", value=page.slug) %}
{% if activities %}
Activities
{% for a in activities %}
{{ a.title }}
{{ a.extra.date }} · {{ a.extra.level }}
{% endfor %}
{% endif %}
{% endblock %}
layouts/activity.html — links back to its location and host:
{% extends "base.html" %}
{% block main %}
{{ page.title }}
{{ page.extra.date }} · {{ page.extra.duration }} · {{ page.extra.level }}
{{ page.content | safe }}
{% set location = all_pages | filter(attribute="slug", value=page.extra.location) | first %}
{% set host = all_pages | filter(attribute="slug", value=page.extra.host) | first %}
{% if location %}
Location: {{ location.title }}
{% endif %}
{% if host %}
Guide: {{ host.title }}
{% endif %}
{% endblock %}
layouts/location.html — aggregates activities and guides for this location:
{% extends "base.html" %}
{% block main %}
{{ page.title }}
{{ page.content | safe }}
{% set activities = all_pages | filter(attribute="section", value="/activities")
| filter(attribute="extra.location", value=page.slug) %}
{% if activities %}
Activities here
{% for a in activities %}
{{ a.title }}
{{ a.extra.date }} · {{ a.extra.level }}
{% endfor %}
{% endif %}
{% set guides = all_pages | filter(attribute="section", value="/users")
| filter(attribute="extra.location", value=page.slug) %}
{% if guides %}
Guides based here
{% for u in guides %}
{{ u.title }}
{% endfor %}
{% endif %}
{% endblock %}
layouts/magic-circle.html — a free-form view that aggregates all three entity types:
{% extends "base.html" %}
{% block main %}
{{ page.title }}
Locations
{% for loc in all_pages | filter(attribute="section", value="/locations") %}
{{ loc.title }}
{% set count = all_pages | filter(attribute="section", value="/activities")
| filter(attribute="extra.location", value=loc.slug) | length %}
{{ count }} activit{{ count | pluralize(singular="y", plural="ies") }}
{% endfor %}
Upcoming Activities
{% for a in all_pages | filter(attribute="section", value="/activities") | sort(attribute="extra.date") %}
{{ a.title }}
{{ a.extra.date }} · {{ a.extra.level }}
{% endfor %}
Guides
{% for u in all_pages | filter(attribute="section", value="/users") %}
{{ u.title }}
{{ u.extra.role }}
{% endfor %}
{% endblock %}
Wiring up the layouts
Each content file needs to reference its layout in frontmatter. Add layout: to the files that use a custom layout (the section _index.md is a good place for this):
users/_index.md
users/alice.md — add layout: user-profile:
activities/morning-kayak.md — add layout: activity:
locations/lake-constance.md — add layout: location:
Result
| URL | Content |
|---|---|
/users/alice | Alice's bio + her location link + her activities |
/activities/morning-kayak | Activity details + location link + guide link |
/locations/lake-constance | Location description + all activities + all guides there |
/magic-circle | Three-column overview of all entities |
Every link is resolved at render time by filtering all_pages — no foreign keys to declare, no joins to configure.
Filtering reference
| Goal | Template expression |
|---|---|
| All pages in a section | all_pages | filter(attribute="section", value="/users") |
| Page with a specific slug | all_pages | filter(attribute="slug", value="alice") | first |
| Pages matching a scalar field | all_pages | filter(attribute="extra.location", value=page.slug) |
| Sort by a field | … | sort(attribute="extra.date") |
| Count matches | … | length |
| First match or nothing | … | first (returns undefined if empty — guard with {% if … %}) |