Routing
Astro uses file-based routing under src/pages/. Every .astro, .md, .mdx, .ts, or .js file in that directory becomes a route.
Static routes
src/pages/index.astro -> /
src/pages/about.astro -> /about
src/pages/blog/index.astro -> /blog
src/pages/blog/first.md -> /blog/first
Dynamic routes
Use [param] brackets for dynamic segments:
src/pages/blog/[slug].astro -> /blog/:slug
src/pages/[...path].astro -> catch-all (404, custom routing)
Static mode (default)
Dynamic routes require getStaticPaths() to tell Astro which pages to build:
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
---
<h1>{post.title}</h1>
<p>{post.body}</p>
This is equivalent to Next.js generateStaticParams().
On-demand mode
For server-rendered routes, skip getStaticPaths() and read params directly:
---
// src/pages/blog/[slug].astro
export const prerender = false; // opt out of static
const { slug } = Astro.params;
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json());
if (!post) {
return Astro.redirect('/404');
}
---
<h1>{post.title}</h1>
Rest parameters (catch-all)
---
// src/pages/[...path].astro
const { path } = Astro.params;
// path = "a/b/c" for /a/b/c
---
Endpoints
.ts or .js files in src/pages/ become API endpoints:
// src/pages/api/posts.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = async () => {
const data = await fetch('https://api.example.com/posts').then(r => r.json());
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' },
});
};
Endpoints support GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, and ALL.
For static builds, endpoints run at build time and produce static files. For on-demand routes, they run per request.
Redirects
In config
// astro.config.mjs
export default defineConfig({
redirects: {
'/old': '/new',
'/blog/[...slug]': '/articles/[...slug]',
},
});
Programmatic
---
return Astro.redirect('/destination', 301);
---
Middleware
// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware((context, next) => {
if (context.url.pathname === '/daily/today') {
const today = new Date().toISOString().split('T')[0];
return context.redirect(`/daily/${today}`);
}
return next();
});
404 pages
Create src/pages/404.astro for a custom 404 page. In static mode, it generates a 404.html file. In on-demand mode, it renders for unmatched routes.
Trailing slashes
Configure in astro.config.mjs:
export default defineConfig({
trailingSlash: 'always', // or 'never' or 'ignore'
});
Comparison with Next.js routing
| Feature | Next.js (App Router) | Astro |
|---|---|---|
| File location | app/ | src/pages/ |
| Dynamic params | [slug]/page.tsx | [slug].astro |
| Catch-all | [...slug]/page.tsx | [...slug].astro |
| Static generation | generateStaticParams() | getStaticPaths() |
| Layouts | layout.tsx (nested) | Manual via layout components |
| API routes | route.ts | .ts files exporting HTTP methods |
| Middleware | middleware.ts in project root | src/middleware.ts |
| Redirects | next.config.js redirects | astro.config.mjs redirects or Astro.redirect() |
| 404 | not-found.tsx or notFound() | 404.astro |