Skip to content
Astro as routing 4 min read

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 whose params.slug is undefined. 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.astro and a dynamic [slug].astro both win over [...path].astro, so a catch-all never shadows your real pages.

Rest vs. single dynamic segment

Feature[slug].astro[...path].astro
Segments matchedExactly oneZero or more
Contains slashesNoYes
Matches route rootNoYes (param === undefined)
Typical useSingle blog postNested docs, file trees, 404s
Route priorityHigherLowest

Best practices

  • Always guard for undefined — the spread matches zero segments, so the root case must be handled explicitly.
  • Prefer getStaticPaths for 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 params directly from content-collection ids so URLs mirror your folder structure for free.
  • Validate or sanitize the captured string before using it in fetch or 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.
Last updated June 14, 2026
Was this helpful?