AstroShowcase
Docs / Core Concepts / File-Based Routing
Core Concepts Updated May 7, 2026

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.

FileURL
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')
---