SGDG

Hi! My name is SyL 👋
This is where I invite you to explore my journey as a solo entrepreneur. Join me on this adventure 🚀, where I share valuable insights and experiences to empower your entrepreneurial path. 🌟

Create a front page with articles listing and pagination for your Nuxt blog in 2023

I'll show you how to create an articles listing page with pagination using query content.

Written by
SyL
Published on
06 Oct 2023

Introduction

👋 Hi all, in this article we will continue our enhancement of our blog created in an hour by adding a front page that lists our articles with pagination using the Nuxt Content composable Query Content.

This is how it will look like at the end.

Blog Listing Page

Check out our article here to style beautiful typography using Tailwind CSS Typography.

Getting Started

Make sure you have a Nuxt 3 blog installed with Nuxt Content. You can also refer to our article here to create a blog in one hour or just download the source file there to begin. Let's fire up our blog with the following command in our project and start working on it!

pnpm run dev

Article list

To begin, we will be creating the component of our article listing where we show the article's date, title and description with a link to the main article.

You can see in the image above that the date is formatted as 'yesterday' and I think that looks better normal date format. To do this we will be using the VueUse pacakge that has a collection of composition utilities with one that will help us to format the date. To install the VueUse package, run the following command in a terminal inside your project folder.

pnpm i -D @vueuse/nuxt @vueuse/core

Once completed, make sure to add the package inside your nuxt.config.ts file.

//nuxt.config.tsexport default defineNuxtConfig({  modules: ["@nuxt/content", "@nuxtjs/tailwindcss", "@vueuse/nuxt"],});

we will now create an ArticleList.vue component under our components folder and insert the following:

//components/ArticleList.vue<script lang="ts" setup>import { ParsedContent } from "@nuxt/content/dist/runtime/types";defineProps<{  article: ParsedContent; // Receive an article object to work with}>();</script><template>  <div class="flex w-full flex-col gap-3">    <NuxtLink :to="article._path" :title="article.title"> <!-- Nuxt component that works like an <a> tag -->      <div class="mb-2 font-medium text-orange-700">        {{ useTimeAgo(article.date).value }} <!-- VueUse function that allows us to format date as time ago -->      </div>      <h2 class="mb-3 text-lg font-semibold hover:text-orange-700 lg:text-2xl">        {{ article.title }}      </h2>      <p v-if="article.description" class="leading-6 text-gray-600 ">        {{ article.description }}      </p>    </NuxtLink>  </div></template>

✨ We have successfully created the component to display our article and we can now work on our pagination!

Pagination

Let's begin by creating a SitePagination.vue component under our components folder and insert the following:

//components/SitePagination.vue<script lang="ts" setup>defineProps<{  currentPage: number;  totalPages: number;  link: string;}>();</script><template>  <ul class="mx-auto gap-3 lg:flex justify-center">    <li v-for="page in totalPages" :key="page">      <NuxtLink        :to="`${link}?page=${page}`"        :class="currentPage === page ? 'bg-gray-100' : ''"        class="inline-flex rounded-md px-3 py-1 hover:bg-gray-200"      >        {{ page }}      </NuxtLink>    </li>  </ul></template>

As you can see in the code, we will be using ?page=${page} query parameter to build our link so our link will look like http://localhost:3000/?page=1 in the end.

✨ We have all the required components now and it's time to piece them together!

Font page with article listing

Let's build our front page now that will list all our articles with pagination. Create an index.vue page under pages folder. This will override our [...slug].vue page and serve as the default page for our blog. Insert the following:

//pages/index.vue<script lang="ts" setup>useSeoMeta({ //A Nuxt composable to set our page title and description  title: "Starter blog created with Nuxt 3 Content and Tailwind CSS",  description: "An open source content by SGDS written by SyL.",});const route = useRoute(); //A Nuxt composable for us to retrieve the page number from the URLconst perPage: number = 1;const pageNo = ref<number>(Number(route.query.page ?? 1)); //Retrive the page number from the URLconst { data: articlesCount } = await useAsyncData(  "articles-count",  () =>    queryContent() //A Nuxt composable that to fetch our articles      .count() //Get the total article counts);const totalPages: number =  articlesCount && articlesCount.value! > 0    ? Math.ceil(articlesCount.value! / perPage)    : 0; //Calculate our total pages for paginationconst { data: articles } = await useAsyncData(  "articles",  () =>    queryContent()             .sort({ date: -1 }) //Sort our articles by descending date      .limit(perPage)  //Limit and skip our articles for pagination      .skip(perPage * (pageNo.value - 1))      .find(),  {    watch: [pageNo], //Watch for the page no changes and refresh our query  });watch(  () => route.query.page, //Watch for the page no and update it  async (page) => {    pageNo.value = Number(page);  });</script><template>  <div class="my-24">    <div class="mx-auto mb-20 flex max-w-3xl flex-col justify-start gap-12">      <ArticleList v-for="article in articles" :article="article" />    </div>    <SitePagination      v-if="totalPages"      :per-page="perPage"      :totalPages="totalPages"      :currentPage="pageNo"      link="/"    />  </div></template>

✨ Now refresh your blog at http://localhost:3000 and you should see the listing and pagination like our result image above! If your styling looks different, check out our article here to style beautiful typography using Tailwind CSS Typography.

Understanding our code

Let's break down the code below, we use useRoute() to get the route object that contains the page number under route.query.page. We also assign the number of article per page under perPage as 1. You can change the value to show more articles per page.

const route = useRoute(); //A Nuxt composable for us to retrieve the page number from the URLconst perPage: number = 1;const pageNo = ref<number>(Number(route.query.page ?? 1)); //Retrive the page number from the URL

We then use useAsyncData and queryContent composable to retrieve the total count of articles necessary for our pagination.

const { data: articlesCount } = await useAsyncData(  "articles-count",  () =>    queryContent() //A Nuxt composable that to fetch our articles      .count() //Get the total article counts);

We use the same way to retrieve all the articles, sort them by date and paginate the data with limit and skip. But here, we use the watch property to actively watch for the changes in page number and refetch the articles again.

const { data: articles } = await useAsyncData(  "articles",  () =>    queryContent()             .sort({ date: -1 }) //Sort our articles by descending date      .limit(perPage)  //Limit and skip our articles for pagination      .skip(perPage * (pageNo.value - 1))      .find(),  {    watch: [pageNo], //Watch for the page no changes and refresh our query  });

✨ And that's how we create a listing page with pagination for our blog!

What's next?

Download the complete code here at Github.

Check out more related articles below!

Create a Nuxt 3 Content blog with Tailwind CSS in one hour.
Elevate your Nuxt blog with Tailwind Typography and Front-matter.
Add dark mode to your Nuxt 3 app in 15 mins.
Add table of contents to your Nuxt 3 blog in 10 mins.
Deploy your Nuxt 3 app with Vercel for free in 5 mins