How to Create a Blog with Vue and Nuxt content

Koen van Zeijl
September 14, 2020 7 min read
Koen van Zeijl

Introduction

After toying with the idea in my head for a while I decided to write a blog myself.

The purpose of my blog is to write interesting articles on various ICT topics. And I know, I’m not the first one. But maybe, just maybe people do like my style of writing. The only way I know to check this is to begin. — Or how Nike would say it “Just do it!” —

De stack

I started looking for a way to set up a blog site that uses a CMS to easily post new articles. For this, I decided to use a JAM stack. JAM stands for Javascript, API and Markup. The various HTML pages are generated in advance and placed on the server. I wanted to write my blogs in markdown so that they — along with the other code for the website — are also kept in the repository. Because of this I actually skip the A step, which gives me a JM stack. Or is it a JMM stack because of markdown? Who knows.

Here are my personal reasons why I chose a JAM stack:

  • It is fast and easy to create a custom website
  • JAM is a new concept for me through which I can use to acquire new knowledge
  • It can be hosted serverless, which means less setup, maintenance and money

The plan and the challenge

This is what I had in mind for my blog:

  • Superfast rendering
  • Optimized SEO
  • Build with NuxtJS
  • Blogs written in Markdown
  • Serverless hosting on hosting on Netlify
  • Comments handled by Disqus

In short, I’ll create a blog with NuxtJS that will be hosted on Netlify. The most important challenge is to make all the dynamic elements work smoothly in the background. With dynamic elements I mean: tags, categories and some SEO-specific subjects like creating a sitemap.

Why NuxtJS?

I already used VueJS in several projects and it works perfectly! For this project, my plan was again to use VueJS and when you want to create a static website, using NuxtJS is a logical choice to combine with VueJS. After doing further research on NuxtJS, it turned out that blogs could be written in markdown by using nuxt/content. With this, I had accomplished one part of my plan.

The installation and configuration process

Installing NuxtJS

Before you can create a NuxtJS project you need to be able to use npx te kunnen gebruiken. Npx comes standard with NPM >5.2.0. Now we just need to create a NuxtJS project with the help of the CLI. Run the following command in your desired project folder npx create-nuxt-app <project-naam>. During this process, you’ll choose your package manager, linting, CSS framework and test framework options. Then choose nuxt/content o be able to create markdown files and select Static for the rendering mode. That’s it, you now have a NuxtJS website!

You can see your site by running the following commands:

$ cd <project-name>
$ npm run dev

Your first post

Create a folder, within the content folder, that will contain all of your blogs. I named mine /content/articles/ . The example below shows you how a first blog could look like. I named my first blog my-first-blog.md

/content/articles/my-first-blog.md
---
title: Create a blog with VueJS and NuxtJS
short: How do you create an SEO optimized static file website with NuxtJS and markdown files, together with an sitemap.xml and tag pages.
image: /images/my-header-image.jpg
tags:
  - code
  - nuxt
  - markdown
---

## My blog title

Your usual **markdown** content. 

As you can see in the example, the header section uses YAML. In this header, information is written that can be reused on other pages. This can be very useful to show a short description of your blog on the blogs overview page. For the tags section, I create dynamic pages to display all blogs with the same subject/tags. This allows the user to find relevant blogs very easily.

The structure of the URL

Within a NuxtJS project, the URL is automatically generated based on the folder and file structure within the /pages folder. I preferred to have my blogs in a separate folder in the case I also want to post my personal projects on the site. This is why I chose the following website structure:

koenvanzeijl.nl                     /// Homepage
koenvanzeijl.nl/blog                /// See all posts
koenvanzeijl.nl/blog/my-first-blog  /// Single blog
koenvanzeijl.nl/tag/vue             /// All relating posts

To achieve this URL structure you’ll need to set up your project as follows:

pages/index.vue             /// Homepagina
pages/blog/index.vue        /// Bekijk alle posts
pages/blog/_slug.vue        /// Enkel blog
pages/tag/_slug.vue         /// Voor alle gerelateerde blogs

Files named _slug.vue will pick up all requests in it’s a folder with the actual passed as a parameter within params.slug.

Single blog file

Below is a simplified example blog page for pages/_slug.vue. It fetches the image, tags and title from the markdown meta information.

pages/_slug.vue
<template>
  <div>
    <div class="post-head">
      <h1>{{article.title}}</h1>
      <img :src="article.image" v-if="article.image">
      <div>
        <div class="date">{{article.createdAt}}</div>
        <div class="tags">
          <span v-for="tag in article.tags" :key="tag">
            <nuxt-link :to="'/tag/'+tag">\#{{tag}}</nuxt-link>
          </span>
        </div>
      </div>
    </div>
    <nuxt-content :document="article" />
  </div>
</template>
<script>
  export default {
    async asyncData ({ $content, params }) {
      const article = await $content('articles', params.slug).fetch()
      return article
    }
  }
</script>

After creating this file, you’ll be able to see your first blog by navigating to http://localhost:3000/blog/my-first-blog . Congratulations! You created your first blog. Now we need just a little more explanation about what it all does. In the _slug.vue file, the blog is fetched by the function asyncData(). This method defines which blog to retrieve by the passed through params.slug parameter. After fetching the blog, article can be used within your view and header data can easily be extracted from it.

What is striking is that I’m using article.createdAt while it is not placed in the header YAML. This is one of the properties that nuxt/content automatically passes.

These are the properties that can be retrieved by default utilizing nuxt/content:

  • body: Blog text
  • dir: The folder that contains your blog
  • extension: The blog file extension (.md in this example)
  • path: Path to the blog
  • slug: The blog slug
  • toc: An arrays that contains the table of content
  • createdAt: The creation date
  • updatedAt: The last updated date

Blogoverzicht

To view all blogs (/pages/blog/index.vue), the nuxt/content api is used to retrieve data. Below is another example:

/pages/blog/index.vue
<template>
  <div>
     <h1>My blog posts:</h1>
     <ul>
        <li v-for="article in articles" :key="article.title">
           <nuxt-link to="#">{{article.title}}</nuxt-link>
        </li>
     </ul>
  </div>
</template>
<script>
  export default {
    async asyncData ({ $content, params }) {
      const articles = await $content('articles', params.slug)
        .only(['title', 'description', 'image', 'slug'])
        .sortBy('createdAt', 'asc')
        .fetch()

      return articles
    }
  }
</script>

The posts can be easily sorted by using sortBy() from the nuxt/content module. You can expand this view by, for example, placing the description in the v-for loop.

The tags

The template of the tag page is almost identical to the page above. The only difference is that the tags are filtered in the asyncData() method.

/pages/tag/_slug.vue
<script>
export default {
  async asyncData ({ $content, params }) {
    const articles = await $content('articles')
      .where({
        tags: { $contains: params.slug }
      })
      .fetch()

    return {
      articles
    }
  }
}
</script>

The sitemap

Finally, we need a sitemap to easily index the blog. However, the dynamic blog pages make this a bit more difficult so we have to install another NuxtJS module to make this happen. Namely, nuxt/sitemap. Run the following command npm install @nuxtjs/sitemap to install nuxt/sitemap.

After installing the sitemap module, you need to edit the nuxt config file and you’re done.

nuxt.config.js
{
  ...
  modules: [
    '@nuxt/content',
    '@nuxtjs/sitemap' <-- Add this to the modules
  ],
  ...
  sitemap: {
    path: '/sitemap.xml',
    hostname: 'https://www.koenvanzeijl.nl',
    async routes () {
      const { $content } = require('@nuxt/content')
      const files = await $content({ deep: true }).only(['path']).fetch()

      return files.map(file => file.path === '/index' ? '/' : file.path)
    }
  },
}

That’s all! We have just created a blog site with a page for each specific blog and a page for the blog overview. We also generate an entry for each of these pages in the sitemap.xml. Now we run the command npm run generate, which will create a dist folder and place your published website there, and you can host the website wherever you want! In another story, I explain how I hosted my blog on Netlify.

Optimisations for the blog

As you have read, you can probably see that it is a new blog and could use some optimisations. Some ideas are:

  • Optimize website images in NuxtJS
  • Automatically post blogs to Instagram
  • Optimize images within the markdown files

Do you have any ideas/tips or something else fun? Let me know in the comments.