Astro Dynamic Routes — Parameters and Static Generation
In this tutorial, you will learn about Astro Dynamic Routes. We cover key concepts, practical examples, and best practices to help you master this topic.
Learn Astro dynamic routes: define parameters with brackets, generate static paths, use catch-all routes, and pass data to page components.
In this lesson, you'll create dynamic routes using [param].astro syntax, implement getStaticPaths() for static generation, and handle catch-all and nested dynamic parameters.
What You'll Learn
How to define dynamic route parameters, generate static paths at build time, access params in page components, and use rest parameters for catch-all routes.
Why It Matters
Dynamic routes let you create pages for each item in a dataset without writing individual files. A single template generates hundreds or thousands of pages.
Real-World Use
DodaTech's tutorial site uses a single dynamic route [slug].astro to render thousands of tutorial pages from content collection entries.
flowchart LR
A[Product Data] --> B[getStaticPaths]
B --> C["[id].astro Template"]
C --> D[/products/1/]
C --> E[/products/2/]
C --> F[/products/3/]
style B fill:#ff5a03,color:#fff
Basic Dynamic Route
Create src/pages/products/[id].astro:
---
export async function getStaticPaths() {
const products = [
{ id: "1", name: "Widget" },
{ id: "2", name: "Gadget" },
{ id: "3", name: "Doohickey" },
];
return products.map(product => ({
params: { id: product.id },
props: { product },
}));
}
const { product } = Astro.props;
---
<html>
<body>
<h1>{product.name}</h1>
<p>Product ID: {product.id}</p>
</body>
</html>
Output: Astro generates three pages at /products/1/, /products/2/, and /products/3/. Each page contains the product data passed as props.
Dynamic Routes with Content Collections
Combine dynamic routes with content collections:
---
import { getCollection } from "astro:content";
export async function getStaticPaths() {
const posts = await getCollection("blog");
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
Output: A page is generated for every entry in the "blog" content collection at /blog/entry-slug/.
Multiple Parameters
Use multiple brackets for nested params:
---
// src/pages/products/[category]/[id].astro
export async function getStaticPaths() {
const products = [
{ category: "electronics", id: "1", name: "Laptop" },
{ category: "electronics", id: "2", name: "Phone" },
{ category: "books", id: "3", name: "Guide" },
];
return products.map(product => ({
params: { category: product.category, id: product.id },
props: { product },
}));
}
const { product } = Astro.props;
---
<h1>{product.name}</h1>
<p>Category: {product.category}</p>
Output: /products/electronics/1/, /products/books/3/, etc.
Catch-All Routes
Use [...slug].astro for catch-all routes:
---
// src/pages/docs/[...slug].astro
export async function getStaticPaths() {
const docs = [
{ slug: "getting-started", title: "Getting Started" },
{ slug: "guides/installation", title: "Installation Guide" },
{ slug: "guides/advanced/config", title: "Configuration" },
];
return docs.map(doc => ({
params: { slug: doc.slug },
props: { doc },
}));
}
const { doc } = Astro.props;
const segments = Astro.params.slug;
// segments is a string for single-level, or array for nested
---
<h1>{doc.title}</h1>
Output: /docs/getting-started/, /docs/guides/installation/, /docs/guides/advanced/config/.
Rest Parameters
Use [[...slug]].astro for optional catch-all (matches / too):
---
// src/pages/[[...path]].astro
export async function getStaticPaths() {
return [
{ params: { path: undefined }, props: { page: "home" } },
{ params: { path: ["about"] }, props: { page: "about" } },
{ params: { path: ["blog", "post-1"] }, props: { page: "post" } },
];
}
const { page } = Astro.props;
---
<h1>{page}</h1>
Output: / matches the root, /about/ matches single-level, /blog/post-1/ matches nested paths.
Common Mistakes
- Forgetting
getStaticPaths()in dynamic routes: Astro requiresgetStaticPaths()for dynamic routes in static mode. Without it, the build fails. - Returning mismatched params: The
paramsobject keys must match the bracket names in the filename.[id].astroneedsparams: { id: ... }. - Not handling empty catch-all segments: When
[[...slug]].astromatches/,slugis undefined. Check for it before using. - Over-generating pages: If
getStaticPaths()returns thousands of paths, build time increases. Use SSR mode for very large datasets. - Mixing dynamic and static files in the same directory: A
[slug].astrofile and afixed.astrofile in the same directory can conflict. Keep dynamic routes in separate directories.
Practice Questions
What function generates paths for dynamic routes? Answer:
getStaticPaths(). It returns an array of{ params, props }objects.How do you access route parameters in the page component? Answer: Through
Astro.paramsorAstro.props(if passed fromgetStaticPaths).What is the difference between
[...slug]and[[...slug]]? Answer:[...slug]requires at least one segment.[[...slug]]is optional and matches the root path too.Can dynamic routes use content collections? Answer: Yes. Query collections in
getStaticPaths()and pass the entries as props.
Challenge
Create a dynamic route for a "products" content collection with nested categories and product IDs. Generate pages at /products/category-name/product-name/.
Mini Project
Build a documentation site with a catch-all route [...slug].astro that renders Markdown files from a content collection. Include breadcrumb navigation based on the slug segments.
FAQ
What's Next
Learn about Astro SSR Modes to understand server-side rendering, static generation, and hybrid approaches.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro