Rest Parameters
Standard dynamic segments like [slug] only match a single path part. When you need a single route file to handle paths of arbitrary depth — /docs/a, /docs/a/b, /docs/a/b/c — you reach for a rest parameter, written as [...path]. Astro collapses everything after the prefix into one string param, making rest parameters the foundation of documentation sites, file-tree browsers, and catch-all 404 handlers. They are zero-JS by default and resolve entirely at build time (or on the server, for SSR).
Defining a rest parameter
A rest parameter is a file or folder whose name is wrapped in square brackets and prefixed with three dots. The spread captures zero or more path segments and exposes them as a single string under Astro.params.
---
// src/pages/[...path].astro
const { path } = Astro.params;
// /a/b/c -> path === "a/b/c"
// / -> path === undefined
---
<h1>You requested: {path ?? "(root)"}</h1>
The captured value is the joined path without leading or trailing slashes. A request to the route root (matching zero segments) yields undefined, which is why the example above guards with ??.
Generating pages with getStaticPaths
For static (prerendered) sites, every rest route must declare its concrete paths through getStaticPaths. The params object you return supplies the value Astro substitutes for [...path]. Because the segment can contain slashes, you pass the full nested path as one string.
---
// src/pages/docs/[...slug].astro
import { getCollection, render } from "astro:content";
export async function getStaticPaths() {
const entries = await getCollection("docs");
return entries.map((entry) => ({
params: { slug: entry.id }, // e.g. "guides/routing/rest-params"
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
---
<article>
<h1>{entry.data.title}</h1>
<Content />
</article>
An entry id of guides/routing/rest-params becomes the URL /docs/guides/routing/rest-params, all served by this single file. This is exactly how content-driven docs sites map a nested folder structure to clean URLs.
Tip: If you want the route’s own index (e.g.
/docs) to render, include an entry whoseparams.slugisundefined. Returning{ params: { slug: undefined } }produces the prefix-only page.
Combining static and rest segments
Rest parameters compose with ordinary dynamic segments. You can fix part of the path and spread the remainder, which is useful for versioned or namespaced docs.
---
// src/pages/[version]/[...slug].astro
export async function getStaticPaths() {
return [
{ params: { version: "v2", slug: "intro" } },
{ params: { version: "v2", slug: "api/clients" } },
{ params: { version: "v1", slug: undefined } },
];
}
const { version, slug } = Astro.params;
---
<p>Version {version}, page: {slug ?? "home"}</p>
Output:
/v2/intro -> Version v2, page: intro
/v2/api/clients -> Version v2, page: api/clients
/v1 -> Version v1, page: home
Server-side rendering and on-demand pages
With output: "server" (or a per-page export const prerender = false), the rest param is read directly from Astro.params on each request — no getStaticPaths required. This lets you proxy a CMS, file system, or external API where the full set of paths is unknown at build time.
---
// src/pages/files/[...filepath].astro
export const prerender = false;
const { filepath } = Astro.params;
const res = await fetch(`https://cdn.example.com/${filepath}`);
if (!res.ok) {
return new Response("Not found", { status: 404 });
}
const body = await res.text();
---
<pre>{body}</pre>
Custom 404 catch-all
A top-level [...path].astro will match any URL not claimed by a more specific route. Pairing it with prerender = false (or returning a 404 status) gives you a programmatic fallback page.
---
// src/pages/[...path].astro
export const prerender = false;
const { path } = Astro.params;
---
<h1>404 — "{path}" doesn't exist</h1>
<a href="/">Go home</a>
Warning: Rest routes have the lowest priority in Astro’s route ranking. A static file like
src/pages/about.astroand a dynamic[slug].astroboth win over[...path].astro, so a catch-all never shadows your real pages.
Rest vs. single dynamic segment
| Feature | [slug].astro | [...path].astro |
|---|---|---|
| Segments matched | Exactly one | Zero or more |
| Contains slashes | No | Yes |
| Matches route root | No | Yes (param === undefined) |
| Typical use | Single blog post | Nested docs, file trees, 404s |
| Route priority | Higher | Lowest |
Best practices
- Always guard for
undefined— the spread matches zero segments, so the root case must be handled explicitly. - Prefer
getStaticPathsfor known content; reserve SSR rest routes for genuinely unknowable paths. - Keep one canonical catch-all (
src/pages/[...path].astro) for 404s rather than scattering fallbacks. - Derive
paramsdirectly from content-collection ids so URLs mirror your folder structure for free. - Validate or sanitize the captured string before using it in
fetchor file reads to avoid path-traversal issues. - Remember rest routes rank lowest, so add more specific routes when you need to override a single nested URL.