Destiner's Notes

Grouping Posts by Series

17 February 2022

Sometimes, you have a list of related posts that you want to group. It may be a series of guides on a single topic or a group of blog posts where you share the engineering tips. Here, we will implement a “post series” feature so you can create new series and add posts to them.

Let’s start with a template:

npm init astro -- --template blog

Let’s also create a few posts:

touch src/pages/posts/introduction-to-cats.md
touch src/pages/posts/why-cats-are-better-than-dogs.md
touch src/pages/posts/how-much-cats-is-too-much.md

As you can guess, we will create a “cats” series.

Next, set the frontmatter of the newly created pages to add “title” and “series” props:

---
title: Introduction to Cats
series: cats
---

Now we can create a new dynamic page that will generate a page for each of our series. For this tutorial, we will have a single series, but you can easily add more.

mkdir src/pages/series
touch "src/pages/series/[slug].astro"

Tip: you can learn more about dynamic pages in our other post or in Astro docs.

Now is the time to create the series page. Let’s start by providing the getStaticPaths function:

export async function getStaticPaths() {
  const list = ['cats'];

  return list.map((slug) => {
    return {
      params: {
        slug,
      },
    };
  });
}

An important thing to note here is that the series slug shown here ('cats') should be the same as the value you provide in the post’s Markdown (series: cats).

You should be able to open /series/cats page in your browser and see an empty page. Let’s add a list with series posts:

const { slug } = Astro.params;
const allPosts = await Astro.glob<PostMetadata>('../posts/*.md');
const posts = allPosts.filter((post) => post.frontmatter.series === slug);

Finally, add a template for the page:

<html lang="en">
  <head>
    <style></style>
  </head>
  <body>
    <main>
      <h1>Series</h1>
      <div>
        {posts.map((post) => (
          <div>
            <a href={post.url}>{post.title}</a>
          </div>
        ))}
      </div>
    </main>
  </body>
</html>

And just like that, you have a list with all posts of the series! As you can see, there is no link to “Hello, World!” page here since it’s not part of the series.

Here’s the entire source code of the series page:

---
export async function getStaticPaths() {
  const list = ['cats'];

  return list.map((slug) => {
    return {
      params: {
        slug,
      },
    };
  });
}

const { slug } = Astro.params;
const allPosts = await Astro.glob<PostMetadata>('../posts/*.md');
const posts = allPosts.filter((post) => post.frontmatter.series === slug);
---

<html lang="en">
  <head>
    <style>

    </style>
  </head>
  <body>
    <main>
      <h1>Series</h1>
      <div>
        {posts.map((post) => (
          <div>
            <a href={post.url}>{post.title}</a>
          </div>
        ))}
      </div>
    </main>
  </body>
</html>

From here, you can style the page, add more series, or add more posts to the series.