Route360

Showing previous/next posts on a Astro blog page

Table of Contents

This post is how to show the previous/next posts on an Astro + Markdown blog page.

It can be adapted for some headless CMS and their API, I think.

The steps would be as follows;

  1. Sort all posts by the date they were published
  2. Get the total index number of the current post from 1
  3. Get the total index number of the previous/next post from 1
  4. Even if the next or previous post doesn't exist, create an empty record
  5. Show both/either of the previous/next posts if they exist

Working environment

  • Astro v2.0.11

Structure of the project

In this entry, I am developing an Astro + Markdown blog with the following structure.

[slug].astro under pages is the post template, and the post URL would be something like https://example.com/[slug]/.

src/
├─ components/
    └─ prevNext.astro
├─ pages/
    └─ [slug].astro
├─ posts/
    ├─ first-post.md
    ├─ second-post.md
    └─ ...
├─ utils/
    └─ sortByDate.js
  • /components/prevNext.astro Previous/Next posts Component
  • /pages/[slug].astro Single post template
  • /utiles/sortByDate.js function to sort posts by date

YAML frontmatter of a Markdown Post

The YAML frontmatter of a post is defined as follows;

---
title: This is my first post
slug: first-post
date: 2023-01-01
---

Creating a function to sort posts by date

First, prepare a function to sort posts by date. You may already know this from Next.js or something.

/src/utils/sortByDate.js
export const sortByDate = (a, b) => {
  return new Date(b.frontmatter.date) - new Date(a.frontmatter.date)
}

Since this example is with Markdown posts, I assign frontmatter.date to the new Date() argument. If you are using any API or GraphQL, assign the appropriate value.

Generate previous/next posts within the post template

Inside [slug].astro, the post template, you generate previous/next posts along with the current post.

Since this is an example for a Markdown blog, I pass frontmatter as the properties of the previous/next posts. If either doesn't exist, pass an empty frontmatter. In case of an API, pass slug or another value instead.

/src/pages/[slug].astro
---
import { sortByDate } from "./utils/sortByDate.js"

export async function getStaticPaths() {
  const allPosts = await Astro.glob("../posts/*.md")
  const numberOfPosts = allPosts.length // Total of all posts

  return allPosts.sort(sortByDate).map((post, i) => ({ // Map() all posts after sorting
    params: {
      slug: post.frontmatter.slug,
    },
    props: {
      post,
      prevPost: // Previous post
        i + 1 === numberOfPosts // If the current post is the oldest
          ? { frontmatter: ""}
          : allPosts[i + 1],
      nextPost: // Next post
        i === 0 // If the current post is the newest
          ? { frontmatter: "" }
          : allPosts[i - 1],
    },
  }))
}

// Get properties (frontmatter) for the previous/next posts
const { prevPost, nextPost } = Astro.props
---

Once you get the previous/next props from Astro.props, show them within the same template.

/src/pages/[slug].astro
---
// ...
const { prevPost, nextPost } = Astro.props
---

// Previous post (if exists)
{prevPost.frontmatter && <a href={`/${prevPost.frontmatter.slug}/`}>{prevPost.frontmatter.title}</a>}

// Next post (if exists)
{nextPost.frontmatter && <a href={`/${nextPost.frontmatter.slug}/`}>{nextPost.frontmatter.title}</a>}

Using a custom previous/next posts Component

In most cases, you will want to use a custom component for previous/next posts.

To do this, first create a component named prevNext.astro in the component directory.

/src/components/prevNext.astro
---
const { prevPost, nextPost } = Astro.props
---

// Previous post (if exists)
{prevPost.frontmatter && <a href={`/${prevPost.frontmatter.slug}/`}>{prevPost.frontmatter.title}</a>}

// Next post (if exists)
{nextPost.frontmatter && <a href={`/${nextPost.frontmatter.slug}/`}>{nextPost.frontmatter.title}</a>}

Then import this component within the [slug].astro post template and pass the the previous/next properties.

/src/pages/[slug].astro
---
import PrevNext from "components/PrevNext.astro" // Importing the component

// ...

const { prevPost, nextPost } = Astro.props
---

// Passing the properties
<PrevNext {prevPost} {nextPost} />

That's that!