Building My New Blog | Nuxt Vs Gridsome

Posted on Sun Jan 17 2021
vue nuxt gridsome

🥅 The Goal

I had three criteria’s for my new blog :

  1. I wanted to use as a CMS for my blog.

  2. I wanted my blog to be statically rendered for performance, SEO and to keep the cost of hosting free by hosting it on Netlify.

  3. I had legacy blogs in markdown files which I wanted to host along with the rest of the blog.

To achieve this, I experimented with both Nuxt and Gridsome. This is my experience with both frameworks.

🎬 Take 1 - Nuxt

First, addressing my old markdown blogs. Nuxt recently released the content module which was perfect for rendering my old markdown files into individual pages.

To build the rest of the blog, Nuxt has a new Full Static mode which was released in version 2.14.0.

This mode used in conjunction with Nuxt’s asyncData hook means it is possible to fetch all blogs post via the API at build time. When we run npm run generate to build the blog, Nuxt auto-magically pre-renders each page of your Nuxt site into static HTML pages.

How this works at high level. In my .src/pages/index.vue file, the home page of the blog, I fetch a list of all published posts from the API using the /articles/me endpoint. This data fetching is done within the asyncData hook and this endpoint returns an array of your published posts. With the data retrieved, I list each of the posts, and link to a new page by making use of the <nuxt-link> router component. I re-use the post slug for the sub-paths of each of the blogs, like so:

<div v-for="(post, index) in posts" :key="index">
	<nuxt-link :to="`/${post.slug}`">{{ post.title}}</nuxt-link>

Nuxt handles routing based on the file structure of your pages directory, so by creating a _slug.vue file relative to where the index.vue file is. Nuxt knows that this will be the template for any sub paths off that page.

Within _slug.vue, we make use of asyncData lifecycle hook again to make another call to the API, which will retrieve the specific blog based on the slug using the /articles/{username}/{slug} endpoint. This returns an object of the specific post and one of the properties is body_markdown. This is the raw markdown of your post. Converting this to HTML and making it look nice is another challenge within itself, but once done you can render this directly in the Vue template using Vue’s v-html directive. You can see how I did it here (warning very messy code!!).

What’s key here is that when building this application in static mode, Nuxt will perform all the actions in the asyncData hook at build time!

Although I was happy with the end result. I realised there is a lot of logic in the asyncData lifecycle hook. Data fetching, markdown parsing and potentially extra data cleaning logic which I would need to implement later. I also had plans to extend the data fetching to alternative sources such as other Forem sites, Medium and GitHub. I felt like this could get pretty unwieldy if I didn’t adopt a nice pattern. This is what lead me to Gridsome.

🎬 Take 2 - Gridsome

The main selling point of Gridsome is the GraphQL data layer. Gridsome provides a simple API to import data from any external source into a data layer. This normalises all your content into a user-friendly GraphQL API.

Also, Gridsome has a growing number of source plug-ins which are helpers to pull data from an external source and import it into the GraphQL data layer.

To read in my old markdown blogs I was able to make use of the filesystem source plug-in. This allows me to create pages for each of my old markdown blog with minimal effort.

Next was to connect Gridsome to There were already plug-ins for this but I decided to hand roll my own as a learning exercise 🤓.

I was able to copy most of my data fetching logic from the Nuxt version of my blog, but I had to write some additional code to import the posts into GraphQL using Gridsome’s Data Store API. You can see how I did this here.

Once all my blogs are loaded in the data layer we can work with this data in any .vue file in the Gridsome project via the <page-query> or <static-query> blocks. Within this block, you can write a GraphQL query and the result is exposed in your <template> via the $page object. The Gridsome docs explain this a lot better!

Similar to Nuxt, Gridsome also has a ./src/pages/ directory and also it’s own router component, g-link. This is how you list the titles of all your posts and link to them:

    <div v-for="blog in $page.articles.edges":key="">
	 <g-link :to="blog.node.slug">{{ blog.node.title }}</g-link>

  query {
    articles: allDevToArticles{
      edges {

Whereas Nuxt crawls your entire application to figure which pages it needs to generate. Gridsome is smart enough to generate a page for each node in for all your GraphQL collections*.

*The aggregation of all blogs in the Gridsome data layer, is referred to as a “collection” each blog is referred to as a “node”.

To work with these pages we create a .vue file in the ./templates directory in the Gridsome project. This is the equivalent of the _slug.vue file in the Nuxt version of the blog.

The name of this template file should be the same as the collection name. Though this can be configured to your liking in gridsome.config.js.

With that setup, I now had parity between both Nuxt and Gridsome implementations of my blog.

Closing Thoughts

Why I Chose Gridsome Over Nuxt

Overall I felt like the architecture for Gridsome was much better suited for my blog. I liked that there is a separation of concerns with data fetching. All of this logic was contained in my source plug-in. This meant I only needed to focus on the organisation of pages and design in the Gridsome codebase.

Nuxt Is Still Great

Cons of Gridsome

This was a quick comparison between two great Vue.js meta frameworks. Are you planning to work with Nuxt or Gridsome any time soon? Let me know.

If you’re reading this on, you check out my new blog site here 👇