Creating a Static Blog with Vue for Free - Part 4

Creating a Static Blog with Vue for Free - Part 4

by Jason Lewis on Oct 13, 2019

SITE NAME CHANGE: Hi there, this is just a quick note to explain why the site name seems to have changed. I've decided that my personal domain of Gecko8 wasn't nearly descriptive enough. I want to cover not just code, but how to make our lives better as developers. With that goal in mind, I feel Life And Code is a better choice. I hope you agree!

This article is part four in an ongoing series on creating a blog site similar to this one. It covers building the actual list of blog posts and displaying the posts themselves.

For Part 3 which talks about setting up the Gridsome project and integrating NetlifyCMS, see Creating The Project.

Alright, in Part 3 of this series we created our Gridsome project and integrated NetlifyCMS, defining our blog content structure in the process. Let's actually show the blog posts already!

Cleaning Up The Template

Let’s start by reorganizing our template a bit. We’re going to leave the About Us page as is since that’s not the important part of this blog. We’ll re-purpose the Home page to be our list of blogs. Later we’ll create the page for displaying an individual post.

Creating the Blog Post List

Setup the Vue File

Open up the src/pages/Index.vue file. Go ahead and delete everything in the template. You can also change the title within the metaInfo section of the script to something like “My Blog”. And finally, delete everything within the style tags. You should be left with something like:

<template>
</template>
 
<script>
export default {
  metaInfo: {
    title: 'My Blog'
  }
}
</script>
 
<style>
</style>

Setup GraphQL

After the template, add another section called page-query. This is a special Gridsome section where we’re going to define out GraphQL interface. Gridsome is GraphQL driven at heart so it has these special constructs in place.

<page-query>
</page-query>

Next, add a query section inside there

<page-query>
query Posts {
}
</page-query>

There’s nothing particularly important here, we’re just defining a query named Posts (name can be anything). Finish it off as follows (don’t worry we’ll go through it all after).

<page-query>
query Posts {
  blogs: allPost {
    edges {
      node {
        id
        title
        summary
        author
        date
        banner
        path
      }
    }
  }
}
</page-query>

Here’s what each piece does:

  • blogs: An optional alias to the collection. We could leave it out but I feel it makes things easier to read in the template.
  • allPost: A special name created by combining the word “all” with the typeName as defined in the @gridsome/source-filesystem' plugin definition in our gridsome.config.js. IMPORTANT This detail isn’t documented anywhere that I could find and had me going for quite a while.
  • edges and node: are terms used by GraphQL to organize data. I won’t get into that here but this article does a good job of explaining where all this terminology comes from if you’re interested: Explaining GraphQL Connections
  • Everything inside the node property are our post properties.

So that’s our GraphQL definition out of the way.

On To The Template

This is going to be a work of art so wait for it…

<Layout>
  <h1>My Blog Posts</h1>
  <div v-for="post in $page.blogs.edges" :key="post.node.id">
    <h2>{{ post.node.title }}</h2>
    <p>{{ post.node.summary }}</p>
  </div>
</Layout>

I know right, that must have taken me forever! Seriously though, all we’re really doing is looping through the list of blogs ($page is a Gridsome property for accessing the GraphQL) and the rest is rest is just accessing the collection of nodes and displaying a div with an h2 and paragraph for each post. As in all Vue v-for statements, you want to have a :key property with a unique value. We could use the index with something like:

<div v-for="(post, index) in $page.blogs.edges" :key="index">

but since each blog post has a unique ID, we should use that.

Now, if you run it again with

yarn develop

or

npm run develop

You should see something like:

Blog List Page

Sorting By Date

Just a quick aside to setup sorting so they appear in descending order of when they were created (newest to oldest).

In your Index.vue file, change the line in the from

blogs: allPost {

to

blogs: allPost(sortBy: "date") {

Save the file and it should refresh, showing the posts in the order 3, 2, 1. At least that’s the order my data should appear in.

Displaying the Blog Post

Okay, this is where things get a little bit weird. Up until now, all of our pages have been pre-determined by our site design. For example, there will always only be one page listing the blogs so we can define its route ahead of time. For the posts themselves, we need to define the routes when we compile. In order to create these routes, Gridsome requires that pages with dynamic routes be defined in the templates folder and not in the pages folder. The name of the template is also important. The correct file for our purposes will be Post.vue since our collection is named Post (from typeName in gridsome.config.js as mentioned previously).

So let’s add a Post.vue file to the src/templates folder.

Here’s the template section for the file

  <Layout :title="$page.blog.title">
    <Header :post="$page.blog" />
    <div>
      <div v-html="$page.blog.content" />
    </div>
  </Layout>
</template>

Most of this is pretty straightforward with the possible exception of the v-html directive in the content div. NetlifyCMS returns the content of the post Markdown as HTML. It basically goes through the Markdown and converts it to HTML, replacing things like headers with h1, h2, etc. tags and things like that. This makes it much easier for us to display. v-html is a Vue directive that renders the content as pure HTML in the div. If we were to just bind to $page.blog.content in the content of the div with something like:

<div>{{ $page.blog.content }}</div>

we would get an ugly mess since Vue by default will HTML encode string data. This is a security measure. So basically, by using v-html, we’re telling Vue that we know what we’re doing and stop messing with our stuff!

Next, let’s add the bit.

<page-query>
query BlogPost ($path: String!) {
  blog: post (path: $path) {
    title
    content
    banner
    author
    date
    keywords
    summary
  }
}
</page-query>

This is similar to what we saw before with a few key differences. The whole purpose of the $path bits is to pull the correct post for the page. As near as I can figure out, the ($path: String!) in the query line tells the query to reference the $path variable (created by Gridsome). The (path: $path) bit in the blog: post line tells the query itself to use the path to find the correct post. What you’re not seeing here is that the blog entity contains a path property that it can compare against. I know, simple, right? This is still one area where my understanding is fuzzy but it works.

The rest of that is pretty much as we saw before.

Now let’s add the script section:

<script>
export default {
    metaInfo() {
        return {
            title: this.$page.blog.title
        } 
    }
};
</script>

Yep, that’s it. All we’re doing is setting the page metaInfo title field to the title of the blog. This is used by Gridsome and doesn’t really impact what we’re doing here.

Now we just need to link to this page somehow! Open up your src/pages/Index.vue file. Change the template to look like this

<template>
  <Layout>
    <h1>My Blog Posts</h1>
    <g-link v-for="post in $page.blogs.edges" :key="post.node.id" :to="post.node.path">
      <h2>{{ post.node.title }}</h2>
      <p>{{ post.node.summary }}</p>
    </g-link>
  </Layout>
</template>

All we changed was the div became a g-link with a :to directive. g-link is a Gridsome link. It’s like a regular HTML a tag except that it tells Gridsome a few extra things so it can build the link url appropriately. The :to directive tells Gridsome where to find the path to use for the link. That’s it.

Now if you look at your site in the browser you should see that the list of blogs is now links and clicking on your test blog … drum rolllllll … opens a page like this:

Blog Post Page

SUCCESS! We now have a blog site! Granted, not a pretty one but hey, this isn’t a CSS course.

Now don’t forget to commit everything to GIT so it can be deployed to your live site on Netlify!

In the next post in the series we'll be talking about adding meta information for SEO since a blog isn't very effective if no-one can find it!