RSTRust-First Site ToolkitA Rust-first static site generator with typed collections, explicit routes, and a format-agnostic asset pipeline.

Blog

Generated Routes, Pagination, and Route Metadata

Generated route families keep taxonomies, sitemap pages, and other computed outputs explicit without pushing route bookkeeping into the site program.

Computed routes should still read like ordinary Rust

Static routes are not enough for a real site.

Sooner or later you need outputs such as:

  • paginated sitemap pages

  • taxonomy indexes

  • archive pages

  • generated JSON indexes

The problem is not whether the engine can produce those routes. The problem is whether the site code stays readable while doing it.

The public API surface

The main API pieces are:

  • SiteBuilder::generated_routes(...)

  • RouteSourceContext

  • GeneratedRoute<T>

  • GeneratedRoute::file(...)

  • GeneratedRoute::with_source_entry(...)

  • GeneratedRoute::with_attribute(...)

  • paginate(...)

  • RouteInfo

Together, these form the route-family slice of the API.

Generated route families are the escape hatch

generated_routes(...) exists for route sets whose size depends on loaded content.

Its route-source closure runs at build time, after collections load but before route rendering.

That closure receives a RouteSourceContext, which can read collections and return a list of GeneratedRoute<T> values.

If the closure returns a plain error, the framework reports it as a structured diagnostic tied to the generated_routes(...) registration site instead of emitting a low-context build failure.

This keeps the site code explicit:

  • gather the data you want

  • decide which routes should exist

  • return the route list directly

There is no hidden DSL inventing route paths for you.

Pagination stays narrow on purpose

The pagination helper does less than many frameworks.

It does not generate paths, templates, or route-side effects. It only splits owned items into numbered pages and returns the data.

That narrowness is deliberate.

The helper removes repetitive bookkeeping, but the route definitions still stay visible at the call site:

paginate(entries, 1000)?
    .into_iter()
    .map(|page| GeneratedRoute::new(format!("/sitemaps/{}.xml", page.page_number), page))

That is easier to read than a framework-level mini-language.

Route metadata makes joins practical

Generated routes also carry metadata:

  • with_source_entry(collection, slug)

  • with_attribute(key, value)

Later routes can then inspect ctx.routes() and filter by structured information instead of guessing from string prefixes.

That is what makes patterns like "sitemap index + several sitemap pages" practical without hard-coding path conventions all over the site.

Why not only collection-backed routes?

collection_page(...) remains the right tool for the common "one route per entry" case.

Generated route families exist for the moment when the route set is a derivation of content rather than a direct mirror of one collection.

That distinction matters because it keeps the simplest API simple while still giving the framework a place for more advanced route families.

Where to go next