Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Dynamic routes

A dynamic route is a page where part of the address changes: /posts/first-post, /posts/learning-astro. The parameter is written in the file name in square brackets. Since a static site is built ahead of time, Astro must know every possible value of the parameter, and the getStaticPaths() function returns them.

The post page

Delete src/pages/posts/first-post.astro and create src/pages/posts/[id].astro:

---
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
 
export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { id: post.id },
    props: { post },
  }));
}
 
const { post } = Astro.props;
const { Content } = await render(post);
---
 
<BaseLayout title={post.data.title}>
  <h1>{post.data.title}</h1>
  <p>{post.data.pubDate.toLocaleDateString('en-US')}</p>
  <Content />
</BaseLayout>

Open /posts/first-post and /posts/learning-astro: both pages are built from one file. Links to them are already in the /posts list.

How it works

getStaticPaths() returns an array of objects, one per page:

FieldWhat it holds
paramsAddress parameter values: { id: 'first-post' } gives /posts/first-post
propsData the page receives in Astro.props

Parameter values are also available in Astro.params, for example Astro.params.id. During the build Astro calls the function once and creates an HTML file for every element of the array.

A tag page

The same technique builds tag pages. Create src/pages/tags/[tag].astro:

---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import PostCard from '../../components/PostCard.astro';
 
export async function getStaticPaths() {
  const posts = await getCollection('blog');
  const tags = [...new Set(posts.flatMap((post) => post.data.tags))];
 
  return tags.map((tag) => ({
    params: { tag },
    props: { posts: posts.filter((post) => post.data.tags.includes(tag)) },
  }));
}
 
const { tag } = Astro.params;
const { posts } = Astro.props;
---
 
<BaseLayout title={`Tag ${tag}`}>
  <h1>Posts tagged "{tag}"</h1>
  {posts.map((post) => <PostCard title={post.data.title} url={`/posts/${post.id}`} />)}
</BaseLayout>

Set removes duplicates, and each tag gets its own page. Open /tags/astro and only posts tagged astro are there. A post can link to a tag with <a href={'/tags/' + tag}>.

Several segments

A parameter with three dots, [...slug].astro, captures an address of any depth: /docs/a/b/c. It is used when entries live in nested folders.

Common mistake

People put a value into params that does not match the links on the site, such as a post title with spaces instead of the id. The page is built at one address while links point to another, and the visitor sees a 404. Use the same field, post.id, both in getStaticPaths() and in links.

Thanks for reading!

Donate to the site — we work hard for you.