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

> Learn about Astro's file-based routing system for pages, dynamic routes, and API endpoints

Astro uses **file-based routing** to generate your site's URLs. Every file in your `src/pages/` directory becomes a page on your site, following the file path.

## Basic Routing

Routes are automatically created based on the file structure in `src/pages/`:

```
src/pages/
├── index.astro          → /
├── about.astro          → /about
├── blog/
│   ├── index.astro      → /blog
│   ├── first-post.astro → /blog/first-post
│   └── [slug].astro     → /blog/:slug (dynamic)
└── api/
    └── posts.json.ts    → /api/posts.json
```

<Info>
  Astro supports `.astro`, `.md`, `.mdx`, `.html`, and `.js`/`.ts` files in the pages directory.
</Info>

## Static Routes

Create a new file in `src/pages/` to add a static route:

```astro title="src/pages/about.astro" theme={null}
---
const pageTitle = "About Us";
---

<html>
  <head>
    <title>{pageTitle}</title>
  </head>
  <body>
    <h1>{pageTitle}</h1>
    <p>Learn more about our company.</p>
  </body>
</html>
```

This creates a page accessible at `/about`.

## Dynamic Routes

Dynamic routes use bracket notation `[param]` in filenames to match any value at that position in the URL path.

### Single Parameter

```astro title="src/pages/blog/[slug].astro" theme={null}
---
import BaseLayout from '../../layouts/BaseLayout.astro';

export async function getStaticPaths() {
  return [
    { params: { slug: 'first-post' } },
    { params: { slug: 'second-post' } },
    { params: { slug: 'third-post' } },
  ];
}

const { slug } = Astro.params;
---

<BaseLayout>
  <h1>Blog Post: {slug}</h1>
</BaseLayout>
```

This matches `/blog/first-post`, `/blog/second-post`, etc.

### With Props

Pass additional data to each route using the `props` object:

```astro title="src/pages/blog/[id].astro" theme={null}
---
export async function getStaticPaths() {
  return [
    { params: { id: '1' }, props: { title: 'First Post', author: 'Alice' } },
    { params: { id: '2' }, props: { title: 'Second Post', author: 'Bob' } },
  ];
}

const { id } = Astro.params;
const { title, author } = Astro.props;
---

<article>
  <h1>{title}</h1>
  <p>By {author}</p>
</article>
```

### Multiple Parameters

Use multiple dynamic segments:

```astro title="src/pages/[category]/[item].astro" theme={null}
---
export async function getStaticPaths() {
  return [
    { params: { category: 'electronics', item: 'laptop' } },
    { params: { category: 'electronics', item: 'phone' } },
    { params: { category: 'books', item: 'novel' } },
  ];
}

const { category, item } = Astro.params;
---

<h1>{category}: {item}</h1>
```

## Rest Parameters

Use `[...path]` for catch-all routes:

```astro title="src/pages/docs/[...path].astro" theme={null}
---
export function getStaticPaths() {
  return [
    { params: { path: 'getting-started' } },
    { params: { path: 'guides/installation' } },
    { params: { path: 'api/reference/config' } },
  ];
}

const { path } = Astro.params;
---

<h1>Documentation: {path}</h1>
```

This matches:

* `/docs/getting-started` → `path = "getting-started"`
* `/docs/guides/installation` → `path = "guides/installation"`
* `/docs/api/reference/config` → `path = "api/reference/config"`

<Note>
  Rest parameters can match paths at any depth, making them perfect for documentation or file browsers.
</Note>

## API Routes

Create API endpoints by exporting HTTP method handlers from `.js` or `.ts` files:

```ts title="src/pages/api/posts.json.ts" theme={null}
export async function GET({ params, request }) {
  const posts = await fetchPosts();
  
  return new Response(
    JSON.stringify(posts),
    {
      status: 200,
      headers: {
        'Content-Type': 'application/json'
      }
    }
  );
}

export async function POST({ request }) {
  const data = await request.json();
  
  // Process the data
  await createPost(data);
  
  return new Response(null, {
    status: 201,
    headers: {
      'Location': `/api/posts/${data.id}`
    }
  });
}
```

<Tabs>
  <Tab title="Supported Methods">
    Astro supports all standard HTTP methods:

    * `GET`
    * `POST`
    * `PUT`
    * `PATCH`
    * `DELETE`
    * `OPTIONS`
  </Tab>

  <Tab title="Context Object">
    Each handler receives a context object with:

    ```ts theme={null}
    {
      params: Record<string, string>,  // Route parameters
      request: Request,                // Web Request object
      cookies: AstroCookies,          // Cookie utilities
      redirect: (path: string) => Response,
      url: URL,                        // The request URL
      site: URL,                       // Site URL from config
      generator: string,               // Astro version
      props: Record<string, any>,     // Props from getStaticPaths
    }
    ```
  </Tab>
</Tabs>

## Route Priority

When multiple routes could match a URL, Astro uses a priority system to determine which route to use. From the source code (`src/core/routing/priority.ts`), routes are prioritized as follows:

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

  <Step title="Dynamic routes">
    Routes with parameters like `/blog/[slug].astro` come next.
  </Step>

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

Given these files:

```
src/pages/
├── posts/index.astro      # Matches /posts
├── posts/create.astro     # Matches /posts/create
├── posts/[id].astro       # Matches /posts/123
└── posts/[...slug].astro  # Matches /posts/a/b/c
```

Requests resolve as:

* `/posts` → `posts/index.astro`
* `/posts/create` → `posts/create.astro` (static wins over dynamic)
* `/posts/123` → `posts/[id].astro`
* `/posts/a/b/c` → `posts/[...slug].astro`

## Route Matching

Astro converts file paths into regular expressions for matching. From `src/core/routing/pattern.ts`:

```ts theme={null}
export function getPattern(segments: RoutePart[][]) {
  const pathname = segments
    .map((segment) => {
      return '\\/' + segment
        .map((part) => {
          if (part.spread) {
            return '(.*?)';
          } else if (part.dynamic) {
            return '([^/]+?)';
          } else {
            return part.content
              .normalize()
              .replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
          }
        })
        .join('');
    })
    .join('');

  return new RegExp(`^${pathname}$`);
}
```

<Info>
  **How it works**: File paths are broken into segments, converted to regex patterns, then matched against incoming URLs.
</Info>

## Page vs. Endpoint

<Tabs>
  <Tab title="Pages (.astro)">
    Return HTML content:

    ```astro theme={null}
    ---
    const greeting = "Hello";
    ---
    <html>
      <body>
        <h1>{greeting}</h1>
      </body>
    </html>
    ```
  </Tab>

  <Tab title="Endpoints (.ts/.js)">
    Return data in any format:

    ```ts theme={null}
    export function GET() {
      return new Response(
        JSON.stringify({ message: 'Hello' }),
        { headers: { 'Content-Type': 'application/json' } }
      );
    }
    ```
  </Tab>
</Tabs>

## Practical Examples

### Blog with Pagination

```astro title="src/pages/blog/[page].astro" theme={null}
---
export async function getStaticPaths({ paginate }) {
  const posts = await fetchAllPosts();
  
  return paginate(posts, { pageSize: 10 });
}

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

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

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

### Dynamic API with Database

```ts title="src/pages/api/users/[id].ts" theme={null}
import { db } from '../../../lib/db';

export async function GET({ params }) {
  const user = await db.users.findById(params.id);
  
  if (!user) {
    return new Response(null, { status: 404 });
  }
  
  return new Response(JSON.stringify(user), {
    headers: { 'Content-Type': 'application/json' }
  });
}

export async function PUT({ params, request }) {
  const updates = await request.json();
  const user = await db.users.update(params.id, updates);
  
  return new Response(JSON.stringify(user), {
    headers: { 'Content-Type': 'application/json' }
  });
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Static Routes When Possible" icon="file">
    They're faster and easier to understand.
  </Card>

  <Card title="Organize with Folders" icon="folder">
    Group related pages together for better maintainability.
  </Card>

  <Card title="Type Your API Routes" icon="code">
    Use TypeScript for better type safety in endpoints.
  </Card>

  <Card title="Handle Errors" icon="shield">
    Always return appropriate status codes in API routes.
  </Card>
</CardGroup>

## Learn More

<CardGroup cols={2}>
  <Card title="Layouts" href="/concepts/layouts">
    Reuse common page structures
  </Card>

  <Card title="Content Collections" href="/concepts/content-collections">
    Type-safe content with built-in routing
  </Card>
</CardGroup>
