Add a route and its template
How an agent gives a content type addresses on a site with dev route add, writes the entry and index templates a route needs, and verifies them with describe route and render.
A route gives a type’s documents addresses on one site: an entry page per document (/walks/{slug}) and, optionally, an index page listing them (/walks). A route with no template is refused by check, so the route and its templates land together.
The recipe touches these files, and the engine reads them in this order when a request comes in:
flowchart LR
req["GET /walks/cherwell-towpath"] --> routes["config/sites/main/routes.yaml<br/>uri /walks/{slug}"]
routes --> doc["The published walk<br/>with that slug and locale"]
doc --> chain{"walk/entry.html in<br/>templates/main/?"}
chain -- yes --> site["templates/main/walk/entry.html"]
chain -- no --> shared["templates/_shared/walk/entry.html"]
site --> base["extends base.html"]
shared --> baseSteps
Add the route. --scaffold writes the entry template from the type’s fields. It loads the whole configuration to do so, so export every ${VAR} first (DATABASE_URL, and SIGNING_KEY once engine.yaml names it): with one unset, the verb writes routes.yaml, exits 3 without the template, and a second run says nothing to change. Recover with engine-dmo new template walk/entry.html.
engine-dmo dev route add walk "/walks/{slug}" --index /walks --scaffold --apply # config/sites/main/routes.yaml
routes:
- type: page
id: rte_ajdbyvntec
uri: "/{slug}"
- { type: walk, id: rte_34agwcmxkh, uri: "/walks/{slug}", index: /walks }
home: { type: page, slug: home } Write the index template, which --scaffold does not:
engine-dmo new template walk/index.html Until it exists, check fails with type "walk" has an index route and no walk/index.html on site main. The scaffold is an empty body block with no h1, so render /walks --json reports a page has exactly one h1, this one has 0 until you write it (Listing page).
Replace the scaffolds with real templates. The entry template receives the document as entry and under the type’s key (walk):
{# templates/_shared/walk/entry.html #}
{% extends "base.html" %}
{% block body %}
<article>
<h1>{{ walk.title }}</h1>
{{ walk.photo.figure("hero", priority=true) }}
{% if walk.summary %}<p>{{ walk.summary }}</p>{% endif %}
<dl>
<dt>{{ t('walks.difficulty') }}</dt><dd>{{ walk.difficulty.label }}</dd>
{% if walk.distanceKm %}<dt>{{ t('walks.length') }}</dt><dd>{{ t('walks.distance', 'km', walk.distanceKm|number) }}</dd>{% endif %}
</dl>
{% if walk.body %}<div class="prose">{{ walk.body|ptext }}</div>{% endif %}
</article>
{% endblock %} # config/strings/en.yaml
strings:
walks.difficulty: Difficulty
walks.length: Length
walks.distance: "{km} km" figure("hero") needs a hero transform in config/images.yaml (Responsive images). An empty asset field prints nothing through figure(), img() and picture(), so the call needs no {% if %} of its own; wrap it only when you add markup around it. Write the index template next: Listing page.
Verify
engine-dmo check
engine-dmo describe route /walks/cherwell-towpath
engine-dmo render /walks/cherwell-towpath --json
engine-dmo render /walks --json describe route says which route and template answered, and --json adds the whole context with real values. render --json must show "status": 200, "errors": [] and "a11y": []. A path that matches no published document answers 404 with kind: not_found.
Route options
Edit routes.yaml by hand for anything dev route add does not set; check --fix keeps the ids.
| Key | Use it for |
|---|---|
uri | The entry path. One string for every locale, or a map per locale: { en: "/walks/{slug}", cy: "/teithiau/{slug}" } |
index | The index path, one string or per locale. Needs <type>/index.html |
paginate | Entries per index page |
facets | Indexed fields the index can be filtered by (?difficulty=easy) |
nest: true | Entries nest under their parent’s path (/about/our-history) |
priority | Orders two patterns of one shape, lowest first. Two routes of one shape and priority are refused as ambiguous. Each keeps answering for its own documents, so the number settles the check and hides nothing (DMO directory) |
feed, calendar | An RSS, Atom and JSON feed, or an iCalendar feed, of the route |
home: { type: page, slug: home } names the home page (or home: { type: homepage } for a singleton type). special: { search: /search } routes the search page. routes.yaml lists every key.
Where templates live
Templates resolve along the site’s chain: templates/<site>/ first, then templates/_shared/. Put templates every site uses in _shared, and a site’s own version of one in templates/<site>/ (engine-dmo new template walk/entry.html --site town --from-shared copies it). engine-dmo describe template walk/entry.html --site town says which file wins.
| Template | Rendered for |
|---|---|
<type>/entry.html | A document’s page |
<type>/index.html | The route’s index |
<type>/landing.html | A landing page (engine-dmo), falling back to index.html |
search.html | The search route |
errors/404.html, errors/500.html | Errors |
base.html | The layout every page extends (a convention, not a requirement) |
Add a field
How an agent adds, labels, changes, renames and removes fields on custom and core Meridian types, including select options with labels, relations and boolean filters, with the commands, the YAML they write and the pitfalls of each verb.
Listing page with filters and pagination
How an agent builds a type's index page in Meridian with route facets for filtering, a translated result count, cards and an accessible pager, and verifies the first, last and filtered pages with render.