How I Made a Personal Blog with Vue & Gridsome

14 July, 2020 | 7 min read


I always used a CMS like WordPress or Ghost to host my sites.

But I decided that I should make something 'from scratch' for a change. So I set out to build a personal blog — and I had to create all the features that I wanted "manually".

This is how I did it.

Getting started

The first part was to decide which tools I'd use for the job.

I was already familiar with Vue from a previous project.

But I had heard great things about React as well...

Then I tried React. Didn't take long for me to return to Vue.

It's just so enjoyable to use. So that's what I settled on.

I wanted it to be fast, and I had heard of this thing called static sites.

They were supposedly really fast. And of course, I wanted my site to be fast.

Why are static sites fast?

The content that you serve to the user is already generated.

That means no waiting for database queries. No waiting for template engines. The content is just sent straight to the user.

There are many other benefits. You can read about them here. I'll talk about one of them a bit later; they're easy to host.

Installing everything

I had most of the prerequisites before starting this project because of previous projects. But let's take it from scratch.

You'll need Node.js. Just install it from their site. I'm currently using version 12.14.1.

Alright. Now, Gridsome:

npm install --global @gridsome/cli

At this point, I created a new repository with the gridsome create PROJECTNAME command.

I recommend that you just read the docs to get started. That's what I did.

Which Node Modules to use?

I went through a lot of issues and steps to figure out which Node modules to install. I'll spare you the story and instead just tell you what I use, and why.

  • FontAwesome icons — I use these as social icons.
  • @gridsome/plugin-google-analytics (link) — Google Analytics because I want to track how many people come and go.
  • @gridsome/plugin-sitemap (link) — For SEO. You make a sitemap and use the Google Search Console to let Google know which sites are on your page. It makes it easier for Google to 'index you', which makes it easier for people to find you. Win-win.
  • @gridsome/remark-prismjs (link) — For code highlighting.
  • @gridsome/source-filesystem (link) — My posts are written in markdown. This lets the website 'detect' those post files and feeds it into GraphQL. Using that, I don't have to make a new page from scratch each time I want to post something. There are other sources that you can use, but I found this one to be the simplest to get started with.
  • @gridsome/transformer-remark (link) — This is there so the markdown can be translated into HTML.
  • gridsome-plugin-netlify-cms (link) — I use this to make it easier to manage all the markdown files for my posts.
    • netlify-cms (link) — See above
  • gridsome-remark-katex (link) — I wanted to be able to write math equations on my website that aren't ugly: compared to e = mc^2.
    • remark-html (link) — See above
    • remark-html-katex (link) — See above
    • remark-math (link) — See above
  • gridsome-plugin-tailwindcss (link) — I styled my website with Tailwind. It's a bit more 'manual' compared to frameworks like Bootstrap. But I like it.

And for good measure, here is my package.json dependencies:

"dependencies": {
    "@fortawesome/fontawesome-svg-core": "1.2.28",
    "@fortawesome/free-brands-svg-icons": "5.13.0",
    "@fortawesome/vue-fontawesome": "0.1.10",
    "@gridsome/plugin-google-analytics": "0.1.1",
    "@gridsome/plugin-sitemap": "0.3.0",
    "@gridsome/remark-prismjs": "0.3.0",
    "@gridsome/source-filesystem": "0.6.2",
    "@gridsome/transformer-remark": "0.6.0",
    "gridsome": "^0.7.0",
    "gridsome-plugin-netlify-cms": "1.0.9",
    "gridsome-remark-katex": "0.1.1",
    "netlify-cms": "2.10.50",
    "remark-html": "11.0.2",
    "remark-html-katex": "2.0.1",
    "remark-math": "2.0.1"
  },
  "devDependencies": {
    "gridsome-plugin-tailwindcss": "2.2.48"
  }

Setting up a blog

Setting up the source & transformer

It took a few steps to get this right.

When you want to share articles, you have to tell the @gridsome/source-filesystem (link) plugin where to look.

Just follow the instructions to set it up on the plugin page. I'll wait.

Now we'll want to set some settings for remark. You can copy mine. Just put this into your module.exports in the gridsome.config.js file.

transformers: {
    remark: {
      externalLinksTarget: '_blank', // External links are opened in a new tab
      plugins: [
        '@gridsome/remark-prismjs', // Code highlighing
        'remark-math', // Math
        'remark-html-katex', // Math
        'remark-html', // Math
      ]
    }
  },

These plugins also need themes. I ended up using the xonokai theme for code highlighting. Browse Prism themes here.

This is what I put in my main.js file:

head.link.push({
  rel: "stylesheet",
  href: "https://cdn.jsdelivr.net/npm/[email protected]/themes/prism-xonokai.css",
});
head.link.push({
  rel: "stylesheet",
  href: "https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css",
});

Posting posts

This is the part where we tell the Netlify CMS where to look for posts. And where to put posts.

I followed the instructions on the gridsome-plugin-netlify-cms (link) page. And then the Netlify CMS guide in the Gridsome Docs.

Now that we can use the CMS, it's time to post something.

But wait...

Where will we display our posts?

Displaying posts

There are two things to do.

Currently, the source-filesystem settings in gridsome.config.js look something like this:

{
      use: '@gridsome/source-filesystem',
      options: {
        typeName: 'Post',
        path: './content/posts/**/*.md',
      }
    },

We also set a template for the Post type. It looks something like this:

templates: {
    Post: '/blog/:title',
  }

So the first thing we should do is set up a template.

This is done by making a .vue file in the templates folder. The Gridsome Docs has a great introduction to making these.

This is what I ended up with. Just ignore the NewsletterForm component — that came later.

<template>
  <Layout>
    <div class="post-title">
      <g-link to="/blog" class="font-medium">&larr; Go Back</g-link>
      <h1 class="text-4xl">{{$page.post.title}}</h1>
      <p class="post-date">{{$page.post.date}} | {{$page.post.timeToRead}} min read</p>
    </div>
    <hr class="m-4">
    <div class="post-content">
      <p v-html="$page.post.content" />
    </div>
  <NewsletterForm class="mt-16" />
  </Layout>
</template>

<page-query>
query Post ($path: String!) {
  post: post (path: $path) {
    id
    title
    content
    date (format: "D MMMM YYYY")
    timeToRead
  }
}
</page-query>

<script>
import NewsletterForm from '../components/NewsletterForm';

export default {
  metaInfo() {
    return {
      title: this.$page.post.title
    };
  },
  components: {
    NewsletterForm,
  },
};
</script>

And now, where do we display those posts? I made a Blog.vue page in the pages folder. It looks like this:

<template>
  <Layout>
    <div class="post-list">
      <div v-for="(edge, index) in $page.allPost.edges" :key="index" class="p-4 hover:shadow-xs rounded-lg">
        <g-link :to="edge.node.path" class="noUnderlineOnHover" style="text-decoration:none;">
          <h1 class="text-3xl font-normal" v-html="edge.node.title" />
          <p class="text-lg font-normal noUnderlineOnHover">{{edge.node.date}} | {{edge.node.timeToRead}} min read</p>
          <hr class="mt-2 mb-2 line ml-16 mr-16" />
          <p class="text-lg font-normal italic noUnderlineOnHover" v-html="edge.node.description" />
        </g-link>
      </div>
    </div>
  </Layout>
</template>
<script>
export default {
  metaInfo: {
    title: "Blog"
  }
};
</script>
<style>
  h1 {
    @apply leading-none;
    @apply mb-2;
    @apply mt-2;
  }
  .noUnderlineOnHover:hover {
    text-decoration: none;
  }
</style>

<page-query>
query {
  allPost(order: DESC) {
    totalCount
    edges {
      node {
        id
        title
        timeToRead
        description
        date (format: "D MMMM YYYY")
        path
      }
    }
  }

}
</page-query>

Especially pay attention to the v-for loop. This is I display all of my posts on the page. Way better than making a div for each post, right?

Making it pretty

At this point, I set up a simple homepage in the index.vue file. I created a basic navbar and linked to the blog page.

Creating the navbar took a lot of googling. I wanted it to be just right. Mobile friendly and all. That's all I can encourage you to do, as well.

I made the navbar a component. I put that into my Default.vue layout. Now it's on every page — not just the homepage.

The basic layout of my site is very simple.

  1. Navbar
  2. Content
  3. Footer (which currently doesn't have anything in it)

You can get pretty far with styling your site to your preferences. That's what I did.

Going live

Man. I wanted to do everything. I wanted to host my site in a Docker container connected to other apps using Apache. Make it scalable. Set up the server myself using Digital Ocean.

But it's a static site. That's way overkill. At least for me.

So I ended up going for Netlify. Which is free.

You simply host your site in a repository, and then Netlify will automatically deploy it every time you post something or make an update.

Gridsome has a guide for Deploying to Netlify. And Netlify will help you along when you sign up.

Conclusion

This is all there really is to it. This is how I built the foundation for my personal website. I learned a lot from building it.

I've even built upon it since then; added components and new post types. Played with GraphQL. And so much more.

I encourage you to learn on your own. Be curious. If you have an idea, try to implement it.

It's great to build something that you can be proud of. Something you use.

So if you haven't already, go ahead and get started. Build your own website.

Liked this post? Join the newsletter.