---
title: "File-Based Routing"
description: "The file system IS the router. No config files, no URL mapping — just directories and files."
kind: guide
maturity: evergreen
confidence: high
origin: ai-drafted
author: "Agent"
directedBy: "krow"
tags: [astro, fundamentals]
published: 2026-03-15
modified: 2026-05-31
wordCount: 608
readingTime: 3
series: "learn-astro"
series_order: 2
prerequisites: [astro-mental-model]
url: https://krowdev.com/guide/astro-file-routing/
---
## Agent Context

- Canonical: https://krowdev.com/guide/astro-file-routing/
- Markdown: https://krowdev.com/guide/astro-file-routing.md
- Full corpus: https://krowdev.com/llms-full.txt
- Kind: guide
- Maturity: evergreen
- Confidence: high
- Origin: ai-drafted
- Author: Agent
- Directed by: krow
- Published: 2026-03-15
- Modified: 2026-05-31
- Words: 608 (3 min read)
- Tags: astro, fundamentals
- Series: learn-astro (#2)
- Prerequisites: astro-mental-model
- Content map:
  - h2: The Rule
  - h2: Static Pages
  - h2: Index Pages
  - h2: Dynamic Routes: [...slug].astro
  - h2: What Happens During Build
  - h2: Sources
- Diagrams: Mermaid fences are paired with adjacent ASCII companions in this document (1 Mermaid, 1 ASCII); HTML figures expose rendered SVG plus copyable Mermaid/ASCII source tabs.
- Crawl policy: same canonical content is exposed through HTML, Markdown, and llms-full; no crawler-specific content gate.

## The Rule

If you are new to Astro, start with the [mental model](/guide/astro-mental-model/). After this, the next step is [.astro files](/guide/astro-files/), then [components](/guide/astro-components/) and [layouts](/guide/astro-layouts/).

Every file in `src/pages/` becomes a URL. The directory structure maps directly to the URL path.

```mermaid
graph LR
  accTitle: File-based routing — pages to URLs
  accDescr: Each file in src/pages becomes a URL. index.astro serves the root, about.astro the about page, and the dynamic [kind]/[...slug].astro renders one URL per entry.
  idx["src/pages/index.astro"] --> u1["/"]
  abt["src/pages/about.astro"] --> u2["/about/"]
  dyn["src/pages/[kind]/[...slug].astro"] --> u3["/guide/astro-mental-model/ (dynamic)"]
```
```ascii
src/pages/
├── index.astro            →  /
├── about.astro            →  /about/
└── [kind]/
    └── [...slug].astro    →  /guide/astro-mental-model/  (dynamic)
```

That's it. No routing config. No `urls.py`. No `app.get('/blog/:id')`. The filesystem **is** the router.

:::analogy
This is like how Apache/nginx serve files from a directory. If you put `blog/index.html` in the document root, visiting `/blog/` serves that file. Astro does the same thing, but compiles `.astro` files to `.html` first.
:::

## Static Pages

A file like `src/pages/about.astro` generates exactly one HTML page at `/about/`. You write it, Astro compiles it, done.

```astro
---
// src/pages/about.astro
// This code runs at build time
const title = "About krowdev";
---

<html>
  <body>
    <h1>{title}</h1>
    <p>A developer knowledge base.</p>
  </body>
</html>
```

## Index Pages

`index.astro` in any directory becomes the default page for that path:

- `src/pages/index.astro` → `/` (homepage)
- `src/pages/[kind]/[...slug].astro` → `/guide/astro-mental-model/`

:::analogy
Same as `index.html` in traditional web hosting, or `__init__.py` in a Python package — it's what you get when you navigate to the directory itself.
:::

## Dynamic Routes: `[...slug].astro`

This is where it gets powerful. Square brackets mean "parameterized." The `...` means "catch all path segments."

`src/pages/[kind]/[...slug].astro` can generate:
- `/guide/agentic-coding-getting-started/`
- `/guide/agentic-coding-prompt-patterns/`
- `/guide/astro-mental-model/`

But Astro needs to know **which** pages to generate at build time. That's what `getStaticPaths()` does:

```astro
---
// src/pages/[kind]/[...slug].astro

export async function getStaticPaths() {
  const entries = await getCollection('kb');

  // Return one path per kb entry
  return entries.map(entry => ({
    params: { kind: entry.data.kind, slug: entry.id },   // what goes in the URL
    props: { entry },              // data passed to the page
  }));
}
---
```

:::analogy
Think of `getStaticPaths()` as a Makefile pattern rule. Instead of writing one rule per file:

```makefile
dist/guide/getting-started.html: content/kb/getting-started.md
dist/guide/prompt-patterns.html: content/kb/prompt-patterns.md
```

You write one pattern:

```makefile
dist/guide/%.html: content/kb/%.md
    $(COMPILE) $< -o $@
```

`getStaticPaths()` is telling Astro: "here are all the `%` values — generate one page for each."
:::

## What Happens During Build

When you run `npm run build`:

1. Astro scans `src/pages/`
2. Static pages (`index.astro`, `about.astro`) → compiled once each
3. Dynamic pages (`[...slug].astro`) → `getStaticPaths()` is called → one HTML file per returned path
4. All output lands in `dist/`

```
Build:
  src/pages/index.astro              →  dist/index.html
  src/pages/[kind]/[...slug].astro   →  dist/guide/agentic-coding-getting-started/index.html
                                     →  dist/guide/astro-mental-model/index.html
                                     →  dist/article/welcome-to-krowdev/index.html
                                     →  ... (one per kb entry)
```

**Challenge: Trace the route**

Look at krowdev's actual page files:

```bash
find src/pages -name "*.astro" | sort
```

For each file, write down:
1. What URL(s) does it generate?
2. Is it static (one page) or dynamic (many pages)?
3. If dynamic, what collection does it iterate over?

**Answer**

| File | URL(s) | Type |
|---|---|---|
| `pages/index.astro` | `/` | Static (1 page) |
| `pages/[kind]/[...slug].astro` | `/guide/astro-mental-model/`, `/article/welcome-to-krowdev/`, ... | Dynamic (1 per kb entry) |

---
Previous: [The Mental Model](/guide/astro-mental-model/) | Next: [.astro Files](/guide/astro-files/)

## Sources

- Astro Docs, [Routing](https://docs.astro.build/en/guides/routing/)
- Astro Docs, [Pages](https://docs.astro.build/en/basics/astro-pages/)
- Astro Docs, [Dynamic routes](https://docs.astro.build/en/guides/routing/#dynamic-routes)