# How Astro File-Based Routing Handles Dynamic Routes and Parameter Extraction

> Learn how Astro file-based routing builds dynamic routes and extracts parameters using [slug].astro files and the Astro.params object for efficient URL matching and data retrieval.

- Repository: [Astro/astro](https://github.com/withastro/astro)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Astro generates routes from the `src/pages` directory at build time, converting file names like `[slug].astro` into regular expressions that match incoming URLs and extract parameters into the `Astro.params` object.**

Astro's file-based routing system transforms your `src/pages` directory into a complete routing table without manual configuration. Each file becomes a route pattern, with special bracket syntax defining dynamic segments that capture URL parameters. This article examines how the framework compiles file paths into matching RegExp patterns and extracts values using the core router implementation in the withastro/astro repository.

## Converting File Names to Route Patterns

During the build process, Astro walks the `src/pages` directory and creates a **RouteData** object for every file. The file-system path is transformed into a regular expression stored in `RouteData.pattern`, capable of matching incoming request URLs.

### Static vs Dynamic Segments

Astro distinguishes between static and dynamic route segments through file naming conventions:

- **Static routes** – `src/pages/about.astro` generates the pattern `/about`
- **Dynamic segments** – `src/pages/blog/[slug].astro` generates `/blog/:slug` and stores `slug` in the `RouteData.params` array
- **Catch-all (rest) segments** – `src/pages/[...slug].astro` matches any depth using the pattern `/*slug`
- **Optional catch-all** – `src/pages/[[...slug]].astro` matches the root path plus any deeper paths

For a dynamic file like `[slug].astro`, the generated RegExp becomes `/^\/blog\/([^/]+?)\/?$/` where the capture group extracts the parameter value.

### Pattern Compilation

The route generation logic lives within Astro's compiler, which processes each page file to produce `RouteData` objects containing both the `pattern` (RegExp) and the ordered `params` array. This happens entirely at build time, ensuring zero runtime overhead for route discovery.

## The Router Match Process

When a request arrives, Astro instantiates the **Router** class defined in [`packages/astro/src/core/routing/router.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/core/routing/router.ts). The `match()` method executes a precise sequence to identify the correct route.

### Pathname Normalization

Before pattern matching begins, the router performs several normalization steps:

1. Adds a leading slash and removes duplicate slashes from the pathname
2. Handles configured `base` path stripping
3. Applies trailing-slash redirect logic based on configuration
4. Adjusts for build format (directory vs file) when normalizing

### Pattern Matching Logic

The router searches the sorted route list for the first entry where `pattern.test(pathname)` returns true. The route list is pre-sorted using `routeComparator` from [`packages/astro/src/core/routing/priority.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/core/routing/priority.ts) to ensure deterministic matching when multiple patterns could apply.

```typescript
// Conceptual flow inside Router.match()
const result = routes.find(route => route.pattern.test(pathname));
if (result) {
  return { type: 'match', route: result, params: getParams(result, pathname) };
}

```

## Parameter Extraction with getParams

Once a route matches, Astro calls **getParams** from [`packages/astro/src/render/params-and-props.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/render/params-and-props.ts) to extract dynamic values from the URL. This function maps RegExp capture groups to parameter names using the `RouteData.params` array.

```typescript
export function getParams(route: RouteData, pathname: string): Params {
  const match = pathname.match(route.pattern)!;
  const params: Params = {};

  // route.params holds ordered param names from the file name
  route.params.forEach((name, i) => {
    // capture group 1 corresponds to the first param, etc.
    params[name] = match[i + 1] ?? null;
  });

  return params;
}

```

For `[slug]` patterns, the first capture group becomes `params.slug`. For `[...slug]` catch-all routes, the pattern captures the entire remainder (`/^(.*)$/`), storing a slash-separated string that components can split into segments. Optional catch-alls yield an empty string when nothing is captured, which Astro treats as `undefined`.

## Route Priority and Resolution Order

Because multiple patterns can match a single URL (e.g., a static `/blog/about.astro` and a dynamic `/blog/[slug].astro`), Astro sorts routes using **routeComparator** in [`packages/astro/src/core/routing/priority.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/core/routing/priority.ts). The comparator applies the following precedence:

1. **Static routes** take priority over dynamic routes
2. **More specific dynamic routes** (fewer wildcards) rank higher than less specific ones
3. **Original file order** serves as a final tiebreaker

This deterministic ordering ensures `Router.match()` returns the most appropriate route during the linear search through the route list.

## Code Examples

Access extracted parameters inside Astro components via the global `Astro.params` object:

```astro
---
// src/pages/blog/[slug].astro
// Matches: /blog/hello-world
const { slug } = Astro.params;   // slug = "hello-world"
---
<h1>Blog post: {slug}</h1>

```

Handle catch-all routes by splitting the captured parameter into segments:

```astro
---
// src/pages/[...segments].astro
// Matches: /docs/api/config or /docs/guides/ssr
const segments = Astro.params.segments?.split('/') ?? []; 
// ['/docs', 'api', 'config'] or ['/docs', 'guides', 'ssr']
---
<nav>
  {segments.map(seg => <span>{seg}</span>)}
</nav>

```

For advanced use cases, you can interact with the router programmatically:

```typescript
import { Router } from 'astro/src/core/routing/router';

const router = new Router(routes, {
  base: '/',
  trailingSlash: 'ignore',
  buildFormat: 'directory',
});

const result = router.match('/blog/awesome-post');
if (result.type === 'match') {
  console.log(result.params.slug); // "awesome-post"
}

```

## Summary

- Astro compiles `src/pages` files into **RouteData** objects at build time, transforming file names like `[id].astro` into RegExp patterns with capture groups.
- The **Router** class in [`packages/astro/src/core/routing/router.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/core/routing/router.ts) normalizes incoming URLs and performs pattern matching against the pre-sorted route list.
- **getParams** in [`packages/astro/src/render/params-and-props.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/render/params-and-props.ts) extracts values from RegExp capture groups and exposes them as the `Astro.params` object.
- **routeComparator** ensures static routes and specific dynamic patterns take precedence over catch-all routes during the matching process.

## Frequently Asked Questions

### How does Astro differentiate between static and dynamic routes?

Astro examines file names inside `src/pages` for bracket syntax. Files without brackets generate static routes, while files containing `[param]` or `[...param]` generate dynamic patterns with corresponding regular expressions. The route generation logic stores parameter names in the `RouteData.params` array to map capture groups during request handling.

### What happens when multiple routes match the same URL?

When multiple patterns match, Astro applies **routeComparator** from [`packages/astro/src/core/routing/priority.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/core/routing/priority.ts) to determine precedence. Static routes always win over dynamic ones, and routes with fewer wildcards beat more generic catch-all patterns. The router returns the first match from this sorted list, ensuring predictable behavior for overlapping definitions.

### How are rest parameters ([...slug]) different from optional catch-alls ([[...slug]])?

Rest parameters `[...slug]` require at least one path segment and capture everything remaining in the URL as a single string. Optional catch-alls `[[...slug]]` use a modified pattern that also matches the root path with no segments, yielding an empty string or `undefined` for the parameter when the URL contains no additional path parts.

### Where does parameter extraction occur in the request lifecycle?

Parameter extraction happens inside the router's `match()` method immediately after pattern validation succeeds. The `getParams` function executes `pathname.match(route.pattern)` and iterates through capture groups, assigning values to keys defined in `route.params`. This occurs before the page component renders, making params available immediately in the frontmatter script.