It's been a while since I blogged on my own site. The past few years I've been publishing my posts over at the Contentful blog, but since I will no longer be working full time for Contentful I thought it was about time to get back to my own personal one as well. When I got back to it I realized I had to install Ruby to run Jekyll on my new laptop and it just felt annoying to have to set everything up again... It would be so much more fun to rebuild the blog using something else, right!?

Anyway, I rebuilt the blog using Nuxt which, according to their own description, is "a minimal framework for creating Vue.js applications with server side rendering, code-splitting, hot-reloading, static generation and more!" and I thought it'd be nice to share that experience as a first blog post on the new framework.

To get started with nuxt the easiest way is to run yarn create nuxt-app <project-name> which will set up a skeleton project with all required dependencies and configuration. To this skeleton project I copied my sass files from my old jekyll project and put them in my /assets folder in the newly created nuxt project. This also requires me to add a webpack loader that can translate the sass-files into css. I ran yarn add node-sass sass-loader and this was enough to get everything up and running.

Next up was to move the actual blog posts. Jekyll has a number of conventions when it comes to naming the markdown files for the blogposts, every file was named in the following format yyyy-mm-dd-blog-post-slug.markdown which is kind of annoying. I wrote a small script to renam the files and let them all be named blog-post-slug.md. The blog posts in Jekyll also includes YML front matter that includes some meta data around the post. I wanted to keep all that out of the actual post and moved all the front matter into a posts.js file that I put in in the /store folder.

posts.js

export default [
    {
        title: 'blog-title',
        tags: ['tag', 'tag2'],
        date: '2019-01-01',
        slug: 'blog-slug'
    },
    // ...
]

This array will be the basis of generating the urls and holding the meta data of the posts.

To configure nuxt to generate dynamic url you need to push them into the routes array in the nuxt.config.js. In this case I want to pull the slug property from every object in the posts array.

nuxt.config.js

import posts from './store/posts.js'

module.exports = {
    // ...
    generate: { 
        routes: posts.map(m => '/posts/' + m.slug)
    },
    // ...
}

This tells nuxt that I expect a route for each post in the posts array based on the slug.

There's no blog-view yet though. To get the routes to render something I created two folders in my pages folder /posts/_post, the underscore is the convention in nuxt for dynamic routes. Every file or folder prefixed with an underscore represents a dynamic part of the url. I also created an ìndex.vue file in the folder to act as the template for the posts. I mostly copied the template directly from my posts template in my old jekyll project, but added a script to load the markdown file.

index.vue

export default {
  async asyncData({params, store}) {
    const md = await import(`~/Posts/${params.post}.md`)
    return {
      content: md.default,
      post: store.getters.getPostById(params.post)
    }
  } 
}

I also added a getPostById method to the vuex store getters as well as importing my posts array.

import posts from '~/store/posts.js'

// ...
 getters: {
        getPostById: (state) => (slug) => {
            return state.posts.find(post => post.slug === slug)
          }
    }
// ...

That's more or less the blogs done, but I also had to build the startpage where I display a list of all the blog posts as well as an excerpt of the first few sentences of the blog post. I added another method to my store getters to get the excerpt from a blog post.

import striptags from 'striptags'
// ...
 getters: {
        // ...
        getPostExcerptById: (state) => (slug) => {
            const short = striptags(require(`~/Posts/${slug}.md`)).split(/\s+/).slice(0,25).join(' ') + '...'
            return short
          }
    }
// ...

I added a package called striptags, which I love simply because it has zero (yes, ZERO!) dependencies, to stip out any html tags. Then I grab the 25 first words and return them as a plain string.

Wait though, isn't the post in markdown? It absolutely is, which is why I added the @nuxtjs/markdownit module to my nuxt.config.js file. This allows Vue to automatically convert markdown files into html when they're imported.

nuxt.js.config

// ...
  modules: [
    '@nuxtjs/markdownit',
    '@nuxtjs/feed'
  ],
// ...

I also added the @nuxtjs/feed to be able to output my posts as an rss feed, we'll get back to this in a second.

I then updated my index.vue file with the layout of my start page.

<template>
  <div>
    <div class="home">
      <ul 
        v-for="post in $store.state.posts" 
        :key="post.title"
        class="post-list"> 
        <li>
          <span class="post-meta">{{ post.date }}</span>
          <h2>
            <a 
              :href="'/posts/' + post.slug"
              class="post-link">
              {{ post.title }}</a>
          </h2>
          {{ $store.getters.getPostExcerptById(post.slug) }}
        </li>
      </ul>
    </div>
  </div>
</template>

I then configured up my rss feed in my nuxt.config.js file.

// ...
  feed: [
    {
      path: 'feed.xml',
      async create (feed) {
        feed.options = {
          title: 'Robert Linde',
          link: 'https://robertlinde.se/feed.xml',
          description: 'The blog of Robert Linde, web developer and chess enthusiast. Blogging on .NET, web development and JavaScript.'
        }
        
        posts.forEach(post => {
          const md = markdownit.render(fs.readFileSync(`./Posts/${post.slug}.md`, 'utf8'))
          feed.addItem({
            title: post.title,
            id: post.slug,
            link: 'https://robertlinde.se/posts' + post.slug,
            description: md
          })
        })

      },
      type: 'rss2',
      cacheTime: 1000 * 60 * 15
    }
  ],
// ...

So far so good, the next step was adding code highlighting. A pretty important part of this particular blog. Luckliy nuxt has a great eco system of plugins, extensions and modules and it didn't take me long to find out that the @nuxtjs/markdownit module that I used for the markdown conversion supports the standard markdown-it-highlightjs package. I added this to my nuxt.js.config.

//...
 markdownit: {
    use: [
      'markdown-it-highlightjs'
    ]
  },
//...

Now everything is back to normal and to finalize I added the about page and set everything up to be published to Netlify on push to master.