Building a SEO Friendly Blog: Using Markdown and Front Matter with Nuxt
Tags: seo, nuxt, jamstack, markdown
Published: 2020-10-02 14:58:26

In this series' previous installment, I covered the basics of creating a static generated site with Nuxt. In today's installment, I'll show you how to use Markdown and Front Matter to develop rich content for your blog. 

Before we write any code, the first thing we'll need to do is install a [webpack](https://webpack.js.org/) loader for markdown. In your terminal, while in the project directory, run `npm i frontmatter-markdown-loader` to add this dependency to your `package.json` file. Next, we need to configure Nuxt to know how to load markdown files. In the root directory, create a file named `nuxt.config.js` and add the following code to extend webpack.

```javascript
export default {
  target: 'static',
  build: {
    extend (config, ctx) {
      config.module.rules.push({
        test: /\.md$/,
        use: [{
          loader: 'frontmatter-markdown-loader'
        }]
      })
    }
  }
}
```

Now that we've configured our project to work with markdown, we can create our first article. Directly under the root directory, create a directory named `articles`. Inside of this directory, create a markdown file named `dogs.md`.

```markdown
---
slug: dogs
title: 'Dogs are the Best Animal - Fight Me'
date: '2020-09-25'
tags: dogs,doggo,pupper,floofer,woofters
description: "Whether you call them dogs, doggos, puppers, floofers, or woofters, they are the best animal. I am willing to fight you over this if you say I'm wrong."
---

# Dogs are the Best Animal - Fight Me

Whether you call them dogs, doggos, puppers, floofers, or woofters, they are the best animal. I am willing to fight you over this if you say I'm wrong.

All doggos are a heckin' good fren. Anyone who disagrees is a monster.
```

You'll notice there's a bit of YAML code at the beginning of our markdown file. This is called [front matter](https://assemble.io/docs/YAML-front-matter.html), and it's a way to include metadata with your markdown content.

The next step is to make our slug pull in the correct markdown file based on the URL parameter. While I would generally recommend not dynamically requiring any dependencies in your JavaScript code, we can safely do that here with our markdown file. We know this code will run at the time of generation, so if there are any missing files, we will catch them during the build process.

Inside of `slug.vue` update the `asyncData` function to be the following. 

```javascript
asyncData ({route}) {
  const { params: { slug } } = route
  const markdown = require(`~/articles/${slug}.md`)
  return {
    markdown
  }
}
```

This function grabs the URL's parameter, requires the matching markdown file, and makes the markdown file's results available to the template. The result is an object with two keys, `html` and `attributes`. The `html` key is what it sounds like; it is the HTML representation of your markdown file. The `attributes` key is an object of key/value pairs of all of the metadata you included with front matter.

The last part of wiring this up is to add it to the template. We'll be using `v-html` to do this, but this is a feature of Nuxt that you want to use with caution. This function will insert any HTML it's given into the page, which can leave you vulnerable to an XSS attack. Never use `v-html` to render user-generated content. This feature should only ever be used with trusted content.

To render the contents of the article, update the template to the following code:

```html
<template>
  <article>
    <div v-html="markdown.html" />
  </article>
</template>
```

If you issue the command `npm run dev` and navigate to `http://localhost/blog/dogs`, you should see your article's content.

Now let's update our landing page to dynamically generate a list of articles based on our markdown files. Webpack allows for requiring files via context, meaning you can require all files that match a certain pattern. This is extremely useful for our case as we can get data about every article we've written without manually requiring each file individually.

In your `index.vue` file, add the following to the bottom of your file.

```html
<script>
export default {
  asyncData () {
    const articles = []
    const r = require.context('~/articles', false, /\.md/)
    r.keys().forEach((key) => {
      articles.push(r(key))
    })
    return {
      articles
    }
  }
}
</script>
```

This script is loading all of the articles, putting them into an array, and making them available to the page's template. As we included a `slug` parameter in the article's metadata, we can use that to output an article list on the landing page. Here's the new code for our template.

```html
<template>
  <div>
    <h1>Articles</h1>
    <ul>
      <li v-for="(article, index) in articles" :key="index">
        <nuxt-link :to="`/blog/${article.attributes.slug}`">{{ article.attributes.title }}</nuxt-link>
      </li>
    </ul>
  </div>
</template>
```

If you navigate to `http://localhost:3000` you should now see a single link to the one article we've written. If you were to add another article to the articles directory, it would show up here too.

That wrap's it up for this part! In the next and final part of this series, we'll go into how to use the attributes we set in front matter to give search engines more context about our article.

_Originally published on [jessie.codes](https://jessie.codes/article/seo-blog-frontmatter)_