Dynamic Routes
In Next.js App Router you use [slug]/page.tsx + generateStaticParams() to pre-render dynamic pages at build time. Astro uses the same bracket-filename convention — [slug].astro — paired with a required getStaticPaths() export inside the frontmatter.
Dynamic routes require multiple generated pages — run these examples in a full Astro project (
npm create astro@latest) locally.
Defining a dynamic segment
Section titled “Defining a dynamic segment”// app/blog/[slug]/page.tsx (Next.js App Router)export async function generateStaticParams() { const posts = await getPosts(); return posts.map((post) => ({ slug: post.slug }));}
export default function BlogPost({ params,}: { params: { slug: string };}) { return <h1>Post: {params.slug}</h1>;}---// src/pages/blog/[slug].astroexport async function getStaticPaths() { const posts = await getPosts(); return posts.map((post) => ({ params: { slug: post.slug }, props: { post }, // pass data alongside params }));}
const { slug } = Astro.params;const { post } = Astro.props;---<h1>{post.title}</h1>Key differences to note:
getStaticPaths()is exported from inside the frontmatter — not as a separate file export like Next.js.- Each path object can carry a
propsfield to colocate fetched data with the path — no second fetch needed on the page. Astro.paramsholds the dynamic segment value, equivalent toparams.slugin Next.js.
Accessing params and props
Section titled “Accessing params and props”// Next.js Pages Router equivalent// pages/blog/[slug].tsximport type { GetStaticPaths, GetStaticProps } from 'next';
export const getStaticPaths: GetStaticPaths = async () => { const posts = await getPosts(); return { paths: posts.map((p) => ({ params: { slug: p.slug } })), fallback: false, };};
export const getStaticProps: GetStaticProps = async ({ params }) => { const post = await getPost(params!.slug as string); return { props: { post } };};
export default function BlogPost({ post }: { post: Post }) { return <article><h1>{post.title}</h1></article>;}---// src/pages/blog/[slug].astro// getStaticPaths + data fetching in ONE function:export async function getStaticPaths() { const posts = await getPosts(); return posts.map((post) => ({ params: { slug: post.slug }, props: { post }, }));}
// props arrive via Astro.props (same as any component)const { post } = Astro.props;---<article> <h1>{post.title}</h1> <div set:html={post.body} /></article>File tree for a blog
Section titled “File tree for a blog”flowchart TD pages["src/pages/"] --> blog["blog/"] blog --> index["index.astro"] blog --> slug["[slug].astro"] index --> indexURL["/blog (list all posts)"] slug --> slugURL["/blog/:slug (individual post)"]
Catch-all routes
Section titled “Catch-all routes”Astro supports rest-parameter catch-all routes with [...path].astro, which maps to [[...slug]]/page.tsx in Next.js.
// app/docs/[[...slug]]/page.tsxexport default function DocsPage({ params,}: { params: { slug?: string[] };}) { const path = params.slug?.join('/') ?? ''; return <div>Docs path: {path}</div>;}---// src/pages/docs/[...path].astroconst { path } = Astro.params;// path is a string like "getting-started/intro"---<div>Docs path: {path}</div>