Back

Building a content navigation component in Nuxt

Since I'm writing a blog post every day, I thought it would be great to offer you the ability to navigate between them. So I created a component to navigate between pages.

In this guide I will show you how I built it using Nuxt and its content module.

Fetching the list of posts

The list of blog posts needs to be retrieved when the user navigates to the page. In Nuxt, we use the useAsyncData composable to achieve this.

Since my blog uses the Nuxt Content module, I need to use the queryContent composable to retrieve the content of my markdown files in the blog folder.

Here's what the script section of my Calendar component looks like:

<script lang="ts" setup>
const props = defineProps<{
  day: number
}>()

const { data: navItems } = await useAsyncData(
    `advent-calendar-${props.day}`,
    () => queryContent('blog')
      .only(['title', '_path', 'date'])
      .where({
        date: {
          $gte: `2024-12-01`,
          $lte: `2024-12-31`,
        },
      })
      .sort({ date: 1 })
      .find(),
)
</script>

Now that we have our list of posts, let's see how we can display them.

Displaying the list of posts

To display the list of post, we're going to iterate through the array of blog posts that we fetched with queryContent in our useAsyncData hook.

But to make the navigation clear, we want to display the current blog post differently. This way, it will be less confusing for the reader. So we'll use the useRoute composable to have access to the current path.

Let's first add this to the script section of our component:

<script lang="ts" setup>
// previous code

const { path: currentPath } = useRoute()
</script>

With our list of blog posts and the current path at our disposal, we can now write the code to display the list of posts.

The template section of our component looks like this:

<template>
  <ul>
    <li
      v-for="item in navItems"
      :key="item._path"
    >
      <div>
        {{ toLocaleShortDateString(item.date) }}
      </div>
      <div v-if="item._path === currentPath">
        {{ item.title }}
      </div>
      <NuxtLink v-else :to="item._path" class="link">
        {{ item.title }}
      </NuxtLink>
    </li>
  </ul>
</template>

This code uses a toLocaleShortDateString utility to convert the date from a string in the YYYY-MM-DD format to a readable string.

You can write your own utility, but here's the one I use:

// utils/date.js
import dayjs from 'dayjs'

export function toLocaleShortDateString(date: string) {
  return dayjs(date).format('D')
}

And that's it! Our component is now ready to display the list of blog posts.

Final results

The code of this site is open-source, so you can check out the final code here: Strift/portfolio

You can also see the component in action below this article. 👇

Cover photo by Elena Mozhvilo on Unsplash

Last updated on December 10, 2024.

👋 About the author

I'm Laurent, a freelance developer experience engineer that helps dev tools build great onboarding experiences.

I specialize in technical writing, UX writing, and full-stack development.

✍️ Latest posts

Read my other articles.

💌 Get in touch

Find me on Bluesky, X (formerly Twitter), LinkedIn, and Github.

You can also contact me via email.