Pages and routing

Declare a dynamic route

Capture a URL segment with square brackets when one template renders several related pages.

A blog with fifty posts doesn’t need fifty page files. It needs one template and a way to tell it which post to render. That’s a dynamic route.

You declare one by putting a segment of the filename in square brackets:

src/pages/posts/[slug].astro

The name in the brackets becomes a route parameter. A request for /posts/hello-astro/ matches this file, and slug has the value hello-astro.

Reading the parameter

Inside the page, Astro.params holds the matched values:

---
const { slug } = Astro.params
---

<h1>Post: {slug}</h1>

/posts/hello-astro/ renders “Post: hello-astro”. /posts/islands/ renders “Post: islands”. One file, many URLs.

The parameter is input

slug tells you which route matched. It comes from the URL, and anyone can type anything into a URL.

So treat it as untrusted input. Validate it before you use it in a database query, a file path, or a permission check. A slug like ../../etc/passwd should never reach fs.readFile().

Where does the value come from?

This depends on how the route renders, and it’s the part people miss.

In a static project, Astro builds every page ahead of time. It can’t guess which slugs exist, so you have to tell it. You do that with a getStaticPaths() function that returns the complete list. Without it, the page above doesn’t render at all. Astro stops with an error saying getStaticPaths() is required for dynamic routes. The next lesson fixes that.

In an on-demand route, Astro renders when the request arrives, so the parameter comes straight from the URL. That needs an adapter for your server or host, which we’ll cover in the deployment module.

Rest parameters

Sometimes one parameter must match several segments. Documentation with paths like /docs/guides/routing/dynamic/ is the classic case. Use three dots:

src/pages/docs/[...slug].astro

Now slug is guides/routing/dynamic. Reach for this only when nested paths are really part of your content. For a flat list of posts, the plain [slug] is clearer.

Try this on your project: create src/pages/posts/[slug].astro with the code above and open /posts/hello-astro/. Read the error, then come back after the next lesson and open a slug you did list and one you didn’t. Ask yourself what should happen for the unknown one. Build it, return a 404, or handle it on demand? That decision is what the next lessons are about.

Lesson completed