Routing

epresso uses routes-as-code: every file under pages/ becomes one or more routes. Content stays in content/; pages/ produces URLs.

Page kinds

FileKind
pages/about.mdDirect Markdown page (layout via front matter)
pages/blog/[slug].epSingle-file route (Python frontmatter + Jinja body)
pages/robots.txt.pyEndpoint exporting get() -> (content_type, body)

Route parts

Path segments support static text, {param}, and {...param} (spread):

  
pages/blog/index.md → /blog/pages/blog/[slug].ep → /blog/<slug>/pages/blog/[slug]/index.ep → /blog/<slug>/ (index.ep = directory root)pages/blog/[...slug].ep → /blog/a/b/ (spread captures the rest)

index in a filename maps to the directory root. Clean directory URLs are generated with a configurable trailing slash ([build] trailing_slash).

Endpoints

A .py page that exports get() becomes a static endpoint:

  
pages/robots.txt.py python
def get(): return "text/plain", "User-agent: *\nAllow: /\n"

File-style URLs like /robots.txt skip the trailing slash. data.json and RSS feeds are commonly built this way.

.ep single-file routes

The recommended route format — Python frontmatter + Jinja body in one file. See [The .ep file format](components.md.

  
epresso
---from epresso.routing import Routedef get_static_paths(): return [Route(path=f"/blog/{p.id}/", params={"slug": p.id}, data=p) for p in site.get_collection("posts")]---<article><h1>{{ props.title }}</h1>{{ content | safe }}</article>

A static .ep route with no get_static_paths() renders once at its default path.

site is injected into the frontmatter. A returned Route carries path, optional params, data (the props passed to the template), and an optional cache_key (auto-derived from a content entry's digest when absent, so edits re-render only that path).

Pagination

paginate is a helper that splits entries into numbered page routes:

  
python
from epresso.routing import paginatedef get_static_paths(): return paginate(site.get_collection("posts"), per_page=10, base_path="/blog/")

It produces base_path/ and base_path/page/N/, each route carrying data={"entries", "page", "num_pages", "prev", "next"} and params={"page": n}.

Route object

  
python
from epresso.routing import RouteRoute( path="/blog/hello/", params={"slug": "hello"}, data={...}, # props passed to the template content_type="text/html", body=None, # raw body for endpoints cache_key=None, # incremental identity)

Importable helpers

discover_route_patterns(pages_dir), expand_pattern(pattern, trailing, data_api), parse_route(path), output_path_for(route_path), and paginate(...) are all exported from epresso.routing for programmatic use.

View transitions & prefetch

epresso is a static, multi-page site — links cause a normal full-page navigation. There are no view-transition or prefetch primitives; every URL is a real HTML document you can deep-link and cache. Add client-side niceties (transition animations, link prefetch) yourself in a component <script>.