Skip to content

Routing and pages

Routes are files under routes/. A .stator file is a page; a .ts file is an API route.

routes/index.stator/, routes/cart.stator/cart. Discovery is automatic.

A [name] segment captures into request.params (always strings):

routes/product/[id].stator → /product/p1 → params.id === "p1"

More specific routes win over param routes (/product/new beats /product/[id]).

Access the request in frontmatter via Stator.request:

---
const { params, query } = Stator.request
---

query collapses repeated keys to the first value.

Stator.reads([...]) hands the page live machine instances:

---
import CartMachine from '../machines/cart.ts'
const [cart] = Stator.reads([CartMachine])
---

Wrap pages in a layout component that exposes slots with <children>:

<!-- customer-layout.stator -->
<header><children name="header" /></header>
<main><children /></main>
<CustomerLayout><CartPage cart={cart} /></CustomerLayout>

Set status, headers, or cookies during render via Stator.response:

---
const res = Stator.response
if (!user) res.status = 401
---

Add a pragma to opt the route into SSE:

---
// @stator live
---

Dynamic routes validate their params in the frontmatter and branch — there is no first-class 404 API in 1.0:

---
const found = catalog.bySlug(String(params.slug))
---
{when(!found, () => (
<section>
<h1>Never stocked that.</h1>
<p><a href="/">Back to the shop.</a></p>
</section>
))}
{when(!!found, () => (
/* the real page */
))}

Set a real status code where it matters (crawlers): Stator.response.status = 404 in the frontmatter’s not-found path.