Dynamic routes
Wrap a path segment in square brackets to make it dynamic. pages/blog/[slug].rat matches /blog/anything and exposes the captured value as args.slug (or just slug at the top level) in the page's scope.
One dynamic segment
pages/blog/.rat → /blog/...
The bracketed filename becomes the capture name. args.slug is the captured string for that request — empty captures (e.g. /blog/) miss this route and fall through to a 404.
# pages/blog/[slug].rat
> server
title: 'Blog post'
> page
<h1> [args.slug]
<p> URL was /blog/[args.slug] Mix static and dynamic segments
Folders may interleave freely
Bracket-folders can sit anywhere in the path. Static segments must match literally; bracket segments capture and bind. Multiple captures stack into args by name.
pages/users/[id]/posts/[post_id].rat
# matches /users/42/posts/hello
# args.id == '42'
# args.post_id == 'hello' Read the value directly
Top-level slug bindings
Each capture is also bound at the top level of the scope under its name. [slug] and [args.slug] resolve to the same value — use whichever reads better at the call site.
# pages/blog/[slug].rat
> server
post: main_db.post.get_by_slug(slug)
> page
<h1> [post.title] Next: Main shell — wrapping every page with a shared layout from main.rat.