File-Based Routing
How Astro turns files in src/pages/ into URLs — static routes, dynamic segments, catch-alls, and API endpoints.
How routing works
Astro’s routing is file-based — the path of a file inside src/pages/ becomes the URL.
No router configuration, no imports, no manual route tables.
| File | URL |
|---|---|
src/pages/index.astro | / |
src/pages/about.astro | /about |
src/pages/blog/index.astro | /blog |
src/pages/blog/post.astro | /blog/post |
src/pages/contact.md | /contact |
Dynamic routes
Put a filename segment in brackets to capture a URL parameter:
src/pages/blog/[slug].astro → /blog/anything-here
src/pages/users/[id].astro → /users/42
Access the captured value via Astro.params:
---
// src/pages/blog/[slug].astro
const { slug } = Astro.params
// Use the slug to fetch the right post
const post = await getPostBySlug(slug)
if (!post) return Astro.redirect('/404')
---
<h1>{post.title}</h1>
Static vs dynamic generation
For static sites, dynamic routes need a getStaticPaths() function that tells
Astro which URL values to pre-render:
---
// src/pages/blog/[slug].astro (STATIC mode)
import { getCollection } from 'astro:content'
export async function getStaticPaths() {
const posts = await getCollection('blog')
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}))
}
const { post } = Astro.props
---
<h1>{post.data.title}</h1>
For SSR sites (output: 'server'), you don’t need getStaticPaths() — Astro
renders the page on each request using whatever slug arrives in the URL.
Catch-all routes
Square brackets with ... capture multiple path segments:
src/pages/docs/[...slug].astro → /docs/a/b/c (slug = "a/b/c")
---
const { slug } = Astro.params // e.g. "getting-started/installation"
const parts = slug?.split('/') ?? []
---
API routes (endpoints)
A .ts or .js file in src/pages/ that exports HTTP method handlers becomes an
API endpoint — no HTML, just data:
// src/pages/api/hello.ts
import type { APIRoute } from 'astro'
export const GET: APIRoute = ({ url }) => {
return new Response(
JSON.stringify({ message: 'Hello!', path: url.pathname }),
{ headers: { 'Content-Type': 'application/json' } },
)
}
This file becomes available at /api/hello. Any HTTP method can be exported: GET,
POST, PUT, PATCH, DELETE, HEAD, OPTIONS.
Redirects
Inline redirect
---
// Redirect at the top of frontmatter, before any rendering
if (!user) return Astro.redirect('/login', 302)
---
Config redirect (static, no server needed)
// astro.config.mjs
export default defineConfig({
redirects: {
'/old-page': '/new-page',
'/blog/[slug]': '/posts/[slug]',
},
})
404 page
Create src/pages/404.astro and Astro uses it for all unmatched routes.
In SSR mode, call Astro.redirect('/404') from a page when a resource isn’t found:
---
const item = await getItem(Astro.params.id)
if (!item) return Astro.redirect('/404')
---
URL utilities
Astro exposes several URL helpers in every .astro component:
---
// Full URL of the current page
const url = Astro.url // URL object
const path = Astro.url.pathname // e.g. "/blog/my-post"
const search = Astro.url.search // e.g. "?page=2"
// Read query string params
const page = Number(Astro.url.searchParams.get('page') ?? '1')
---