Create a blog with NuxtJS and Netlify CMS - Part 2

Updated by Tom on

Part 2 of this series focuses on the internals with Nuxt and Netlify CMS. Utilising Netlify CMS, we can have a fully-featured blog without the need for a database or server. If you haven't already read part 1, go check it out!

Again, if you'd prefer to skip this tutorial, feel free to clone the repository from Github instead.

Automating your Netlify CMS posts

One problem I faced while creating this blog was having the homepage generate the posts created in the content/blog folder. I needed to loop through the files in the folder at build time (using nuxt generate) and create a JSON array with the list of posts available. So, here was my solution!

First of all, install the following dependencies:

yarn add front-matter-markdown frontmatter-markdown-loader


npm i front-matter-markdown frontmatter-markdown-loader

Then create index.js and blogs.json files in the content folder with the following content:

const fs = require('fs')
const parseMarkdown = require('front-matter-markdown')

 * getFiles - Get list of files in directory
 * @param {string} dir
 * @returns {Array} Array of objects
const getFiles = (dir) => {
  const files = fs.readdirSync(dir)
  let filelist = []

  files.forEach((file) => {
    const markdownFile = fs.readFileSync(`content/blog/${file}`, 'utf-8')
    const fileContents = parseMarkdown(markdownFile)
    const date =
    const slug = file
      .slice(0, -1)

    const obj = { date, slug }

  return filelist

 * Write blogs json file
const writeBlogs = async () => {
  const fileArray = await getFiles('content/blog/')

  // Order array by date (default asc)
  const sortedArray = await fileArray.sort((a, b) => {
    return -

  // Reverse array and write to JSON
  const reversedArray = await sortedArray.reverse()
  const jsonContent = await JSON.stringify(reversedArray)

  fs.writeFile('content/blogs.json', jsonContent, (err) => {
    if (err) throw new Error(err)

Code block to get markdown files and write blogs

This file uses the fs module to read the files in content/blog and writes the filename and date to the blogs.json file as an array of objects. The array is then reversed to ensure the posts are in the correct order. NOTE: you’ll need to have a published datetime field in your Netlify CMS config.yml for this to work correctly.

Empty JSON file

For now, blogs.json is empty and will populate itself when you’ve written a post. If you’ve already written a sample post, running node content/index.js in your terminal will run the script and you should see the blogs.json file populate with a post!

Finally, to ensure this script runs when you generate your blog, you need to make the following modification to your package.json:

  "scripts": {
    "generate": "node content/index.js && nuxt generate"
Generate script in package.json file

Now, whenever you generate your blog with yarn generate, the JSON file will contain all of your posts!

Creating your homepage

Now you’ve got your blog creating your array of posts; it’s time to set up the rest of your nuxt project to generate your posts and display them on your homepage.

To start, let’s ensure nuxt generates your blog posts. First of all, you'll need to import your JSON file and loop through the posts:

import blogs from './content/blogs.json'

export default {
  generate: {
    routes: [].concat( => `/blog/${blog.slug}`))

  build: {
    extend(config, ctx) {
      // Add this to your build config
        test: /\.md$/,
        loader: 'frontmatter-markdown-loader',
        options: {
          vue: true
Nuxt config file

You’ll notice we’ve also updated the build configuration to include the frontmatter-markdown-loader. This is added so that your markdown files can be parsed in your Vue components.

Speaking of which, it’s now time to create the homepage (pages/index.vue)! I’m not going to show you all components, only how you can use the markdown files to display your posts on the homepage:

import blogs from '~/content/blogs.json'

export default {
  async asyncData({ app }) {
    async function awaitImport(blog) {
      const wholeMD = await import(`~/content/blog/${blog.slug}.md`)
      return {
        attributes: wholeMD.attributes,
        link: blog.slug

    const blogList = await Promise.all( => awaitImport(blog))
    ).then((res) => {
      return {
        blogList: res

    return blogList
Homepage template retrieving the blogs list

If you’ve read Marina’s post, you’ll recognise some of this code. In this case it’s a bit simpler as we’re not creating blog posts in different languages. If that’s something you’re looking to do, check out Marina’s post and make the necessary edits.

The code above gives you access to an array of objects containing the post attributes (title, author etc) and the link. From here, you could add the following to your template block to list your posts:

      v-for="(blog, index) in blogList"
Homepage HTML markup

The PostCard component will then have the postInfo prop to help you display your posts. This prop will contain your slug and post attributes as defined above.

Displaying your posts

Finally, we need to create a page for your blog posts (pages/blog/_slug.vue). This is quite a simple component:

export default {
  async asyncData({ params }) {
    const post = await import(`~/content/blog/${params.slug}.md`)
    const attr = post.attributes
    const slug = params.slug

    const {
    } = attr

    const dateOptions = {
      weekday: 'long',
      year: 'numeric',
      month: 'short',
      day: 'numeric'

    const publishedDate = new Date(date)
    const updatedDate = new Date(update)
    const published = publishedDate.toLocaleDateString('en-GB', dateOptions)
    const updated = updatedDate.toLocaleDateString('en-GB', dateOptions)

    return {
      html: post.html
Blog single page to retrieve single posts

our list of attributes may differ from the above, depending on the fields set in your Netlify CMS config.yml file. In short, the above is take the slug from the router, fetching the markdown and extracting the attributes and post html for displaying. This is achieved by using the frontmatter-markdown-loader package we added and configured earlier.

From here, you can easily add this content to your template:

  <section class="post">
    <h1>{{ title }}</h1>
    <img v-lazy="thumbnail" class="thumbnail" :alt="title" />
    <div class="post-content" v-html="html"></div>
Blog single page HTML markup

Of course, this is a very basic example and you can add all the attributes in whatever way you want. But this will work!

Final thoughts

This has been a very long post but I hope it’s been useful for you. However, there are many improvements that could be made, such as:

  • Create several JSON files for paginated posts.
  • Loop through all directories for other collections (such as portfolio, events etc).
  • Potentially move away from markdown for the posts and instead use JSON.

If you have any questions, contact me on Twitter @tribe_code. I’ll be happy to help!