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.
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.