How Astro File-Based Routing Handles Dynamic Routes and Parameter Extraction
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.astrogenerates the pattern/about - Dynamic segments –
src/pages/blog/[slug].astrogenerates/blog/:slugand storesslugin theRouteData.paramsarray - Catch-all (rest) segments –
src/pages/[...slug].astromatches any depth using the pattern/*slug - Optional catch-all –
src/pages/[[...slug]].astromatches 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. The match() method executes a precise sequence to identify the correct route.
Pathname Normalization
Before pattern matching begins, the router performs several normalization steps:
- Adds a leading slash and removes duplicate slashes from the pathname
- Handles configured
basepath stripping - Applies trailing-slash redirect logic based on configuration
- 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 to ensure deterministic matching when multiple patterns could apply.
// 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 to extract dynamic values from the URL. This function maps RegExp capture groups to parameter names using the RouteData.params array.
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. The comparator applies the following precedence:
- Static routes take priority over dynamic routes
- More specific dynamic routes (fewer wildcards) rank higher than less specific ones
- 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:
---
// 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:
---
// 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:
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/pagesfiles into RouteData objects at build time, transforming file names like[id].astrointo RegExp patterns with capture groups. - The Router class in
packages/astro/src/core/routing/router.tsnormalizes incoming URLs and performs pattern matching against the pre-sorted route list. - getParams in
packages/astro/src/render/params-and-props.tsextracts values from RegExp capture groups and exposes them as theAstro.paramsobject. - 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →