Documentation Index Fetch the complete documentation index at: https://mintlify.com/withastro/astro/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Astro uses file-based routing, where files in src/pages/ automatically become routes on your site. This guide covers common routing patterns and advanced techniques.
File-Based Routing
The structure of your src/pages/ directory determines your site’s URL structure.
Basic Routes
src/pages/
├── index.astro → /
├── about.astro → /about
├── contact.astro → /contact
└── blog/
├── index.astro → /blog
└── first-post.astro → /blog/first-post
Nested Routes
Create nested directories for organized URL structures:
src/pages/
└── products/
├── index.astro → /products
├── categories/
│ └── [category].astro → /products/categories/electronics
└── [id].astro → /products/123
Dynamic Routes
Use square brackets [param] to create dynamic route parameters.
Single Parameter
src/pages/products/[id].astro
---
import { getProduct } from '../../api' ;
const { id } = Astro . params ;
const product = await getProduct ( Number ( id ));
---
< html >
< head >
< title > { product . name } | Online Store </ title >
</ head >
< body >
< h1 > { product . name } </ h1 >
< p > $ { product . price } </ p >
< img src = { product . image } alt = { product . name } />
</ body >
</ html >
Static Path Generation
For static sites, generate all possible paths at build time:
src/pages/products/[id].astro
---
export async function getStaticPaths () {
const products = [
{ id: 1 , name: 'Laptop' , price: 999 },
{ id: 2 , name: 'Mouse' , price: 29 },
{ id: 3 , name: 'Keyboard' , price: 79 },
];
return products . map (( product ) => ({
params: { id: String ( product . id ) },
props: { product },
}));
}
const { product } = Astro . props ;
---
< h1 > { product . name } </ h1 >
< p > $ { product . price } </ p >
Multiple Parameters
src/pages/blog/[year]/[month]/[slug].astro
---
export async function getStaticPaths () {
const posts = [
{ year: '2024' , month: '01' , slug: 'getting-started' , title: 'Getting Started' },
{ year: '2024' , month: '02' , slug: 'advanced-tips' , title: 'Advanced Tips' },
];
return posts . map (( post ) => ({
params: {
year: post . year ,
month: post . month ,
slug: post . slug
},
props: { post },
}));
}
const { year , month , slug } = Astro . params ;
const { post } = Astro . props ;
---
< article >
< h1 > { post . title } </ h1 >
< p > Published: { month } / { year } </ p >
</ article >
Catch-All Routes
Use [...path] to match any number of path segments.
Basic Catch-All
src/pages/[...path].astro
---
const { path } = Astro . params ;
// path could be: "docs", "docs/guide", "docs/guide/routing", etc.
const segments = path ? path . split ( '/' ) : [];
---
< h1 > Path: / { path } </ h1 >
< ul >
{ segments . map (( segment ) => (
< li > { segment } </ li >
)) }
</ ul >
Documentation Site Pattern
src/pages/docs/[...slug].astro
---
import { getCollection } from 'astro:content' ;
export async function getStaticPaths () {
const docs = await getCollection ( 'docs' );
return docs . map (( doc ) => ({
params: { slug: doc . id },
props: { doc },
}));
}
const { doc } = Astro . props ;
const { Content } = await doc . render ();
---
< article >
< h1 > { doc . data . title } </ h1 >
< Content />
</ article >
Named Catch-All with Fallback
src/pages/blog/[...slug].astro
---
import { getCollection , render } 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 ;
if ( ! post ) {
return Astro . redirect ( '/404' );
}
const { Content } = await render ( post );
---
< article >
< h1 > { post . data . title } </ h1 >
< Content />
</ article >
Redirects
Handle redirects for moved content, aliases, and legacy URLs.
Static Redirects
Define redirects in your Astro config:
import { defineConfig } from 'astro/config' ;
export default defineConfig ({
redirects: {
'/old-page' : '/new-page' ,
'/blog/[...slug]' : '/articles/[...slug]' ,
'/temporary' : {
status: 302 ,
destination: '/new-location' ,
},
} ,
}) ;
Dynamic Redirects
Handle redirects programmatically:
src/pages/legacy/[id].astro
---
const { id } = Astro . params ;
// Redirect to new URL structure
return Astro . redirect ( `/products/ ${ id } ` , 301 );
---
Conditional Redirects
src/pages/dashboard.astro
---
const session = Astro . cookies . get ( 'session' );
if ( ! session ) {
return Astro . redirect ( '/login' );
}
---
< h1 > Dashboard </ h1 >
< p > Welcome back! </ p >
API Routes
Create API endpoints that return JSON or other data formats.
Basic API Route
src/pages/api/products.ts
import type { APIRoute } from 'astro' ;
export const GET : APIRoute = async ({ request }) => {
const products = [
{ id: 1 , name: 'Laptop' , price: 999 },
{ id: 2 , name: 'Mouse' , price: 29 },
];
return new Response ( JSON . stringify ( products ), {
status: 200 ,
headers: {
'Content-Type' : 'application/json' ,
},
});
};
Dynamic API Routes
src/pages/api/products/[id].ts
import type { APIRoute } from 'astro' ;
export const GET : APIRoute = async ({ params }) => {
const { id } = params ;
const product = await fetchProduct ( id );
if ( ! product ) {
return new Response ( JSON . stringify ({ error: 'Not found' }), {
status: 404 ,
headers: { 'Content-Type' : 'application/json' },
});
}
return new Response ( JSON . stringify ( product ), {
status: 200 ,
headers: { 'Content-Type' : 'application/json' },
});
};
function fetchProduct ( id : string ) {
// Fetch from database
return { id , name: 'Product' , price: 99 };
}
POST Requests
import type { APIRoute } from 'astro' ;
export const POST : APIRoute = async ({ request }) => {
const data = await request . json ();
// Validate data
if ( ! data . email || ! data . message ) {
return new Response ( JSON . stringify ({
error: 'Email and message are required'
}), {
status: 400 ,
headers: { 'Content-Type' : 'application/json' },
});
}
// Process contact form
await sendEmail ( data );
return new Response ( JSON . stringify ({
success: true ,
message: 'Message sent successfully'
}), {
status: 200 ,
headers: { 'Content-Type' : 'application/json' },
});
};
async function sendEmail ( data : any ) {
// Email sending logic
}
Priority and Route Matching
Astro resolves routes with this priority order:
Static routes
Exact file matches like /about.astro have highest priority.
Dynamic routes
Single parameter routes like /[id].astro are checked next.
Catch-all routes
Catch-all routes like /[...path].astro have lowest priority.
Example Routing Order
src/pages/
├── blog.astro → /blog (1st priority)
├── [page].astro → /about, /contact (2nd priority)
└── [...slug].astro → /anything/else (3rd priority)
Advanced Patterns
Internationalization (i18n)
Structure routes for multiple languages: src/pages/
├── en/
│ ├── index.astro → /en
│ └── about.astro → /en/about
└── es/
├── index.astro → /es
└── about.astro → /es/about
Or use dynamic routing: src/pages/[lang]/[...slug].astro
---
const { lang , slug } = Astro . params ;
const validLangs = [ 'en' , 'es' , 'fr' ];
if ( ! validLangs . includes ( lang )) {
return Astro . redirect ( '/en' );
}
---
Create a custom 404 page: ---
const currentPath = Astro . url . pathname ;
---
< html >
< head >
< title > 404 - Page Not Found </ title >
</ head >
< body >
< h1 > 404 - Page Not Found </ h1 >
< p > The page < code > { currentPath } </ code > doesn't exist. </ p >
< a href = "/" > Go home </ a >
</ body >
</ html >
Best Practices
Organize by Feature Group related pages in directories for better maintainability.
Use Named Parameters Choose descriptive parameter names like [slug] or [id] instead of generic names.
Handle Edge Cases Always validate parameters and handle missing data gracefully.
Leverage TypeScript Use TypeScript for type-safe routing and API routes.
Troubleshooting
Route conflicts
If routes conflict, check the priority order. More specific routes should be in separate files.
Dynamic routes not generating
Ensure getStaticPaths() is exported and returns the correct format.
Redirects not working
Verify your adapter supports redirects. Some static hosts require additional configuration.