> ## 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.

# Routing Patterns

> Learn common routing patterns in Astro including dynamic routes, catch-all routes, and redirects

## 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

```astro title="src/pages/products/[id].astro" theme={null}
---
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:

```astro title="src/pages/products/[id].astro" theme={null}
---
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

```astro title="src/pages/blog/[year]/[month]/[slug].astro" theme={null}
---
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

```astro title="src/pages/[...path].astro" theme={null}
---
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

```astro title="src/pages/docs/[...slug].astro" theme={null}
---
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

```astro title="src/pages/blog/[...slug].astro" theme={null}
---
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:

```js title="astro.config.mjs" theme={null}
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:

```astro title="src/pages/legacy/[id].astro" theme={null}
---
const { id } = Astro.params;

// Redirect to new URL structure
return Astro.redirect(`/products/${id}`, 301);
---
```

### Conditional Redirects

```astro title="src/pages/dashboard.astro" theme={null}
---
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

```ts title="src/pages/api/products.ts" theme={null}
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

```ts title="src/pages/api/products/[id].ts" theme={null}
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

```ts title="src/pages/api/contact.ts" theme={null}
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:

<Steps>
  <Step title="Static routes">
    Exact file matches like `/about.astro` have highest priority.
  </Step>

  <Step title="Dynamic routes">
    Single parameter routes like `/[id].astro` are checked next.
  </Step>

  <Step title="Catch-all routes">
    Catch-all routes like `/[...path].astro` have lowest priority.
  </Step>
</Steps>

### Example Routing Order

```
src/pages/
├── blog.astro              → /blog (1st priority)
├── [page].astro            → /about, /contact (2nd priority)
└── [...slug].astro         → /anything/else (3rd priority)
```

## Advanced Patterns

<Accordion title="Pagination">
  Create paginated routes for large datasets:

  ```astro title="src/pages/blog/[page].astro" theme={null}
  ---
  import { getCollection } from 'astro:content';

  export async function getStaticPaths({ paginate }) {
    const posts = await getCollection('blog');
    const sortedPosts = posts.sort(
      (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
    );

    return paginate(sortedPosts, { pageSize: 10 });
  }

  const { page } = Astro.props;
  ---

  <h1>Blog - Page {page.currentPage}</h1>

  {page.data.map((post) => (
    <article>
      <h2>{post.data.title}</h2>
    </article>
  ))}

  {page.url.prev && <a href={page.url.prev}>Previous</a>}
  {page.url.next && <a href={page.url.next}>Next</a>}
  ```
</Accordion>

<Accordion title="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:

  ```astro title="src/pages/[lang]/[...slug].astro" theme={null}
  ---
  const { lang, slug } = Astro.params;
  const validLangs = ['en', 'es', 'fr'];

  if (!validLangs.includes(lang)) {
    return Astro.redirect('/en');
  }
  ---
  ```
</Accordion>

<Accordion title="Custom 404 Pages">
  Create a custom 404 page:

  ```astro title="src/pages/404.astro" theme={null}
  ---
  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>
  ```
</Accordion>

## Best Practices

<CardGroup cols={2}>
  <Card title="Organize by Feature" icon="folder">
    Group related pages in directories for better maintainability.
  </Card>

  <Card title="Use Named Parameters" icon="tag">
    Choose descriptive parameter names like `[slug]` or `[id]` instead of generic names.
  </Card>

  <Card title="Handle Edge Cases" icon="shield">
    Always validate parameters and handle missing data gracefully.
  </Card>

  <Card title="Leverage TypeScript" icon="code">
    Use TypeScript for type-safe routing and API routes.
  </Card>
</CardGroup>

## Troubleshooting

<Steps>
  <Step title="Route conflicts">
    If routes conflict, check the priority order. More specific routes should be in separate files.
  </Step>

  <Step title="Dynamic routes not generating">
    Ensure `getStaticPaths()` is exported and returns the correct format.
  </Step>

  <Step title="Redirects not working">
    Verify your adapter supports redirects. Some static hosts require additional configuration.
  </Step>
</Steps>
