Categories for a blog built with Nuxt

Creating categories for posts with Nuxt Content

Getting started

To create categories and display them we will be using @nuxt/content. At that point you should have Nuxt installed and some articles created in your project. The structure of root directory should look something like this.

blog-nuxt-categories/
    content/
        articles/
        article1.md
        ...
    layouts/
        default.vue
    pages/
        articles/
            _slug.vue
            index.vue
        index.vue
        ...

You can check out the GitHub repository here.

Installation

If you have your content repository and some articles in it, you probably already have @nuxt/content installed. If not, you can use npm

npm install @nuxt/content

or Yarn

yarn add @nuxt/content

The module property needs to be added to our nuxt.config file.

export default {
  modules: ['@nuxt/content'],
};

Creating post categories

Inside your content/ directory add a categories/ directory.

mkdir content/categories

In the categories/ directory we will create a markdown file for each category.

touch content/categories/my-first-category.md

Now we can add a name in the created my-first-category.md file.

---
name: First Category
---

Adding category to an article

Next we need to start adding categories to posts. In your articles' .md files you probably have YAML front matter block which looks something like this.

---
title: My article
description: Article about creating categories.
---

Add categories to that block.

---
title: My article
description: Article about creating categories.
categories:
    - First Category
---

Displaying categories

We created our categories and added them to the articles. Next, we need to create a separate page for each category where its articles will be displayed. After that, we list the categories on the articles' index page and on the page of each article.

Creating pages for each category

We will create a separate page for each category and display all articles for that category on the page.

To do that first we need to create a template page for categories. Inside your pages/ directory you already should have something like articles/ directory in which you're displaying all your articles. In the pages/articles directory create a category/ directory.

mkdir pages/articles/category

Inside a category/ directory we will create a _category.vue file.

touch pages/articles/category/_category.vue

We will generate a separate page for each category and display articles within that category. For that once again we will use asyncData function.

<script>
export default {
  async asyncData({ $content, params }) {
    const categories = await $content('categories')
      .where({ slug: { $contains: params.category } })
      .limit(1)
      .fetch()
    const category = categories.length > 0 ? categories[0] : {}
    const articles = await $content('articles', params.slug)
      .where({ categories: { $contains: category.name } })
      .fetch()
    return {
      articles,
      category
    }
  }
}
</script>

Now inside <template> we display a category and its articles.

<template>
  <div>
    <div>
      <NuxtLink :to="`/articles/category/${category.slug}`">
        <span>{{ category.name }}</span>
      </NuxtLink>
      <ul>
        <li v-for="article in articles" :key="article.slug">
          <NuxtLink
            :to="{ name: 'articles-slug', params: { slug: article.slug } }"
          >
            <h2>{{ article.title }}</h2>
          </NuxtLink>
        </li>
      </ul>
    </div>
  </div>
</template>

This will generate a page for each category. You should be able to visit http://localhost:3000/articles/category/my-first-category and the page should include the category name and its articles.

Category page

Displaying a category on article's page

Next we will display category on article's page. To do that we will be working in the pages/articles/_slug.vue file. We create two variables: categoriesList and categories.

<script>
export default {
    async asyncData({ $content, params }) {
        const article = await $content('articles', params.slug).fetch();
        const categoriesList = await $content('categories')
            .only(['name', 'slug'])
            .where({ name: { $containsAny: article.categories } })
            .fetch()
        const categories = Object.assign({}, ...categoriesList.map((s) => ({ [s.name]: s })))
        return {
            article,
            categories
        }
    }
}
</script>

Then we write article's category in the <template> part.

<template>
  <div>
    <section>
      <article>
        <div>
          <h1>{{ article.title }}</h1>
          <div>
            <div v-for="(category, id) in article.categories" :key="id">
              <NuxtLink :to="`/articles/category/${categories[category].slug}`">
                <span>{{ categories[category].name }}</span>
              </NuxtLink>
            </div>
          </div>
          <p>{{ article.description }}</p>
        </div>
        <nuxt-content :document="article" />
      </article>
    </section>
  </div>
</template>

Displaying list of categories on articles' index page

To display articles on articles' index page we need to add categories to our asyncData function.

<script>
export default {
    async asyncData({ $content, params }) {
        const articles = await $content('articles', params.slug)
            .only(['title', 'description', 'createdAt', 'slug', 'categories'])
            .sortBy('createdAt', 'desc')
            .fetch()
        const categories = await $content('categories', params.slug)
            .only(['name', 'slug'])
            .sortBy('createdAt', 'desc')
            .fetch()
        return {
            articles,
            categories,
        }
    }
}
</script>

Next we will display the list of categories inside the <template> tag.

<template>
  <div>
    <div>
      <h1>Articles</h1>
      <ul>
        <li v-for="category of categories" :key="category.slug">
          <NuxtLink
            :class="`category ${category.slug}`"
            :to="`/articles/category/${category.slug}`"
          >
            <span> {{ category.name }} </span>
          </NuxtLink>
        </li>
      </ul>
      <ul>
        <li v-for="article of articles" :key="article.slug">
          <NuxtLink
            :to="{ name: 'articles-slug', params: { slug: article.slug } }"
          >
            <h2>{{ article.title }}</h2>
            <p>{{ article.description }}</p>
            <ul>
              <li v-for="category in article.categories" :key="category">
                {{ category }}
              </li>
            </ul>
          </NuxtLink>
        </li>
      </ul>
    </div>
  </div>
</template>

If you want to style each category differently, you can add ${category.slug} to categories :class attribute.

Conclusion.

You should have something like that on your articles' index page. You can also check out a simple GitHub repository for this article here.

Article index page