The one rule
The filesystem IS the router: a file under
src/pages/ becomes a URL. [brackets] = a dynamic
segment; [...rest] = catch any depth; a layout wraps a page via
<slot/>.
There is no routing config to maintain in Astro. Drop a file in
src/pages/ and a route appears. Dynamic routes need
getStaticPaths() in static output. Layouts live in
src/layouts/ by convention — but they are just components
that render <slot/> where the page's content goes.
The routing visualizer — click a URL, see the route + layout
Left: the curated src/ tree (mirrors Astro's rules). Right: type a URL
(or click a sample) and the resolver shows which file matches, the params it
receives, and which layout wraps it. The resolver below is a faithful model of
Astro's match order — static beats named-param beats rest.
[param]
[...rest]
The <slot/> — how a layout wraps a page
A layout is an ordinary Astro component. It renders the page shell
(<html>/<head>/<body>)
plus a <slot/> placeholder. The page imports the layout and
passes its unique markup as the layout's children — Astro injects those children
where the <slot/> sits.
<p>This markup came from src/pages/index.astro</p>
---
import Base from '../layouts/Base.astro';
const { title } = Astro.props; // title is passed INTO the page from its caller
---
<Base title="Home">
<h1>Hello, world</h1>
<p>This markup came from src/pages/index.astro</p>
</Base>
The layout reads its own props with const { title } = Astro.props;.
Layouts nest by importing one layout inside another and wrapping
it the same way — the inner layout's <slot/> receives the
outer page content.
Route priority — who wins when routes collide
When more than one file could build the same URL, Astro sorts by specificity. The resolver above implements this order (static > named param > rest).
| Priority | Rule | Example match |
|---|---|---|
| 1 | Reserved routes (_astro/, _action/, _server_islands/) | internal Astro assets & features |
| 2 | More path segments wins over fewer | /posts/[...slug] beats /[...slug] for /posts/a |
| 3 | Static route beats dynamic | posts/create.astro beats posts/[id].astro for /posts/create |
| 4 | Named param [x] beats rest [...x] | blog/[slug] beats blog/[...path] for /blog/hello |
| 5 | Prerendered dynamic beats on-demand (SSR) dynamic | static-generated page wins over server-rendered match |
| 6 | Endpoints beat pages | data.json.ts beats data.astro |
| 7 | File-based routes beat configured redirects | a real file shadows a redirects entry |
| 8 | Tie → sorted alphabetically by Node's default locale | last resort, deterministic on one machine |
Prefix a file/folder with _ (e.g. _utils.js) to exclude
it from routing — handy for colocating components and tests next to pages.