How to Build Your Own Blog with Nuxt, Nuxt Content, and Cloudinary

Written by terieyenike | Published 2022/05/21
Tech Story Tags: tech-blog | javascript | programming | vue | nuxtjs | cloudinary | blogging-fellowship | hackernoon-top-story | hackernoon-es | hackernoon-hi | hackernoon-zh | hackernoon-vi | hackernoon-fr | hackernoon-pt | hackernoon-ja

TLDRThis article will teach you how to build a blog that has dynamic pages for each article. It will use the Nuxt content module to access markdown, YAML, XML, and even CSV files through an API-like interface. With Cloudinary, we can upload, create, and manage digital experiences across any browser and device. The following are required to complete this tutorial: A Cloudinary account, a knowledge of JavaScript and familiarity with the Vue framework. Creating a blog with Nuxt requires a basic knowledge of the framework and a familiarity with Vue.via the TL;DR App

Having a blog to write and share our knowledge on the internet is essential for visibility and making us known in our field as an authority on a subject.
This article will teach you how to build a blog that has dynamic pages for each article.

Table of Contents

  • Prerequisites
  • Getting Started
  • What is Nuxt?
  • What is Nuxt Content?
  • What is Cloudinary?
  • Creating a Blog with Nuxt
  • Creating the Blog Notes
  • Fetching the Posts
  • Creating Dynamic Pages
  • Final Thoughts
  • Learn More
To follow along, view the codebase and its repo on GitHub and the demo for reference.

Prerequisites

The following are required to complete this tutorial:
  • Cloudinary account. Sign up is free
  • Basic knowledge of JavaScript
  • Familiarity with Vue

Getting Started

To get started, open up the terminal and run the command:
npx create-nuxt-app blog-for-developers
The command above will kick off a command-line interface (CLI) prompt that will allow us to configure various aspects of our project.
Now that the boilerplate is set up with all the files and folders, let’s run the development server which is accessible on
http://localhost:3000
inside the project.
# change directory to project folder
cd blog-for-developers

# start the development environment
npm run dev
What is Nuxt?
NuxtJS is an open-source intuitive Vue framework that allows us to build user interfaces, making web development simple and powerful.
What is Nuxt Content?
During the initial setup of this project, we selected the Nuxt content module. This module lets us use our codebase as a Git-based headless CMS to access markdown, JSON, YAML, XML, and even CSV files through an API-like interface. With this module
@nuxt/content
, it will inject the
$content
instance globally in our project to access it anywhere in a project.
What is Cloudinary?
Cloudinary is a cloud-service image and video management tool for web and mobile applications. With Cloudinary, we can upload, create, and manage digital experiences across any browser and device.

Creating a Blog with Nuxt

Before we create the content for the blog in markdown using Nuxt content, let’s first create the landing page for the blog that will include the navigation links and some text.

Add Font Style
In the
nuxt.config.js file
, we can add our favorite font to the head section and use a particular font family as seen below:
The above code block will have no effect on the page until we write the CSS that will use the font.

Now, add the following code to the
pages/index.vue
which will include the navigation links and some text on the home page. Remove the component,
<Tutorial />
.
<template>
    <section class="showcase">
      <header>
        <h2 class="logo">
          <nuxt-link to="/">ecosurf</nuxt-link>
        </h2>
        <nav class="nav desktop">
          <ul>
            <li class="nav__list">
              <nuxt-link to="/blogs">Stories</nuxt-link>
            </li>
          </ul>
        </nav>
      </header>
      <video
        autoplay
        loop muted
        src="https://res.cloudinary.com/terieyenike/video/upload/v1651393236/mixkit-tropical-island-landscape-view-4692-large_yanvml.mp4"></video>
      <div class="overlay"></div>
      <div class="text">
        <h2 data-type="uppercase" class="stroke">Never Stop</h2>
        <h3 data-type="uppercase">Exploring The World</h3>
        <p>
          View of the tropical island landscape, from a hill with houses, palm
          trees and many trees, and in the distance the hills that surround the
          sea, on a sunny day.
        </p>
      </div>
      <ul class="social">
        <li>
          <a href="https://twitter.com/terieyenike" rel="noopener noreferrer" target="_blank"><img
            alt="twitter profile" src="https://i.ibb.co/Wnxq2Nq/twitter.png"/></a>
        </li>
        <li>
          <a href="https://instagram.com/terieyenike" rel="noopener noreferrer" target="_blank"><img
            alt="instagram profile" src="https://i.ibb.co/ySwtH4B/instagram.png"/></a>
        </li>
      </ul>
    </section>
</template>
In the above code block, we included a
<video>
with the source of the video stored in Cloudinary with attributes such as autoplay, loop, and muted that mute the video when it loads. To obtain the media stored in Cloudinary, we upload our desired video to the media library tab. Thereafter, copy the URL which will be attached to the
src
of the
<video>
.
Also, we referenced the links in the navigation header with the
nuxt-link
component to both the home
/
and the
/blogs
routes which we will create to view all our blogs.

To make sure our page is styled with CSS, create a folder at the root of the project,
assets/styles/main.css
, and include the following from this gist.

In the
nuxt.config.js
file, update the file to include the created CSS file with the following:
export default {
      ...  
      css: ['@/assets/styles/main.css'],
      ...
 }
Here is what our page should look like:

Creating the Blog Notes

To build our blog site, we need to have content to display and render on the
/blogs
page. First, create a
content
directory and then the blog directory that will contain all the markdown files. Writing all the content in markdown supports a lot of the markdown syntax standards such as the headings, links, and code blocks with syntax highlighting for different programming languages.

PS: The Nuxt content utilizes the content folder to store all the files.

Here’s an example of a markdown file in the content directory. Add the following to the file
content/blogs/egghead.md
.
---
    title: Egghead
    cover_image: https://res.cloudinary.com/terieyenike/image/upload/v1651446130/pexels-jeremy-bishop-8241100_oklfpe.jpg
    author: Teri Eyenike
    description: All we need to do is open up our terminal and run the command npm install @nuxt/content. Once it's installed, you'll see that inside of our package.json, we see our Nuxt Content module. Next, to finalize the setup, let's go ahead and open up our nuxt.config.js, and let's go ahead and scroll down to the section that's labeled Modules.
    date: May 2, 2021
    publishOn: 2021-05-02T00:00:00
    tags: ["learning", "platform"]
 ---
    [Egghead](https://www.egghead.io) is a great platform for developers to enhance their skills!
    <!-- more content -->
Markdown files have the ability to add metadata using the concept of front matter which is denoted with the triple-dash syntax at the start and stop of the front matter. The metadata, is what is displayed to readers like the time of publication and excerpt of the blog content, is what is displayed to readers. Other various properties can be included. 
PS: Inside of the front matter, it uses the YAML syntax.
Fetching the Posts
To fetch the posts for the blog page, let’s create a new route under
pages
. Create a folder, called blogs, and in there create
index.vue
file for the directory, blogs.  

Next, copy and add the following code to the
pages/blogs/index.vue
file to display the rendered markdown files.
// pages/blogs/index.vue
    <template>
      <main>
        <header>
          <h2 class="logo">
            <nuxt-link to="/">ecosurf</nuxt-link>
          </h2>
          <nav class="nav desktop">
            <ul>
              <li class="nav__list">
                <nuxt-link to="/blogs">Stories</nuxt-link>
              </li>
            </ul>
          </nav>
        </header>
        <div class="container section">
          <div class="container__grid">
            <div v-for="blog in blogs" :key="blog.slug + blog.createdAt" class="card">
              <img :src="blog.cover_image"
                   alt="blog photographs"/>
              <div class="pad__card">
                <div class="author">
                  <p class="author__name">{{ blog.author }}</p> <span>|</span>
                  <p>{{ blog.date }}</p>
                </div>
                <h2 class="title">{{ blog.title }}</h2>
                <p>{{
                    blog.description.substring(0, 150)
                  }}...</p>
                <button>
                  <nuxt-link :to="`/blogs/${blog.slug}`">Read More</nuxt-link>
                </button>
              </div>
            </div>
          </div>
        </div>
      </main>
    </template>
    <script>
    export default {
      async asyncData({$content}) {
        const blogs = await $content("blogs").sortBy("publishOn", "desc").fetch()
        return {
          blogs
        }
      },
      head() {
        return {
          title: "Read interesting stories as a nomad",
          meta: [
            {
              hid: 'description',
              name: 'description',
              content: 'Daily and juicy content as you learn, work, and relax. WFH'
            }
          ]
        }
      }
    }
    </script>
    <style scoped>
    header {
      background: #111111;
      position: unset;
    }
    .container {
      width: 85%;
      max-width: 75rem;
      margin-inline: auto;
    }
    .section {
      padding: 3em 0;
    }
    .container__grid {
      display: grid;
      gap: 2em;
      grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
    }
    .card {
      background: #f0f7f4;
      box-shadow: 0px 4px 3px rgba(0, 0, 0, 0.1);
      border-radius: 5px;
    }
    .card img {
      object-fit: cover;
      border-top-left-radius: 5px;
      border-top-right-radius: 5px;
      width: 100%
    }
    .pad__card {
      padding: 2em;
    }
    .author {
      display: flex;
      align-items: center;
    }
    .author, .title {
      margin-bottom: 1em;
    }
    .author span {
      margin: 0 0.3em;
    }
    .author__name {
      text-transform: capitalize;
    }
    button {
      border: unset;
      padding: 1em 2em;
      margin-top: 2em;
      background: #0D5159;
      font-weight: 700;
      cursor: pointer;
    }
    button a {
      color: #fff;
    }
    </style>
The code block above does the following:
  • asyncData
    : used to generate our page from Nuxt as it generates individual files statically to users. We pass in the
    $content
    method where the blog notes are stored, the
    sortBy()
    function, and the
    fetch
    method. 
  • sortBy()
    : takes two parameters,
    publishOn
    (sort it by date) and the desc attributes on how you want the content to appear based on the most recent content published
  • To make each blog article dynamic, we will assign it to a URL, where it’s
    /blogs/
    the blog slug on the Read more button using the
    <nuxt-link>
    component.
Rendering the posts
The last step for viewing the snippet of the rendered posts from the content directory is to use the v-for directive and loop over the blogs and render each article in a card.
Here is what the blog page should look like now:

Creating Dynamic Pages

Now let’s create pages for each blog post to have dynamic URL routes. In Nuxt, to create dynamic pages, we append an underscore before the .vue file name so as not to make the URL hardcoded. 

We then create the
_slug.vue
file under the blogs directory and add the following code:
// pages/blogs/_slug.vue
    <template>
      <main>
        <header>
          <h2 class="logo">
            <nuxt-link to="/">ecosurf</nuxt-link>
          </h2>
          <nav class="nav desktop">
            <ul>
              <li class="nav__list">
                <nuxt-link to="/blogs">Stories</nuxt-link>
              </li>
            </ul>
          </nav>
        </header>
        <div class="container">
          <div class="return">
            <nuxt-link to="/">
              <img alt="back to home" src="/home.png"/>
            </nuxt-link>
            <span>/</span>
            <nuxt-link to="/blogs">Blog</nuxt-link>
            <span>/</span>
            <p>{{ note.title.substring(0, 15) }}...</p>
          </div>
          <section>
            <h1>{{ note.title }}</h1>
            <nuxt-content :document="note" cla />
          </section>
        </div>
      </main>
    </template>
    <script>
    export default {
      async asyncData({$content, route}) {
        const note = await $content(`blogs/${route.params.slug}`).fetch()
        return {
          note
        }
      }
    }
    </script>
    <style scoped>
    header {
      background: #111111;
      position: unset;
    }
    .container {
      width: 85%;
      max-width: 75rem;
      margin-inline: auto;
    }
    section {
      padding-bottom: 2em;
    }
    .return {
      display: flex;
      margin: 1.5em 0;
    }
    .return img {
      width: 20px;
      height: 20px;
    }
    .return span {
      margin: 0 1em;
    }
    h1 {
      margin-bottom: 1em
    }
    </style>
This block of code does the following:
  • ${route.params.slug}
    : We use the content to fetch a specific file from the directory blogs using the route parameter and the slug which will map to our actual file name
  • To render the markdown as actual content defined in the markdown file, we use the nuxt-content component and pass the prop of the
    document
    with the entire
    note
    object
  • scoped
    : Defining the scoped to the style element make sure that the styling only applies to a specific page

Final Thoughts

The creation of a blog is great when you want to share your thoughts with a wider audience and Nuxt has provided us with a way to do so in this article. There are still other features we can still explore to make sure we have a robust blog website.
Learn More
Try out the live demo 

Written by terieyenike | I am a software developer focused on creating content through technical writing and documentation.
Published by HackerNoon on 2022/05/21