How I Made a Personal Blog with Vue & Gridsome
Tags: javascript, vue, webdev
Published: 2020-07-18 14:29:54

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](https://github.com/chhoumann/HueControl).

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](https://scotch.io/bar-talk/5-reasons-static-sites-rock). 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](https://nodejs.org/en/). Just install it from their site. I'm currently using version 12.14.1.

Alright. Now, [Gridsome](https://gridsome.org/docs/):

```bash
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](https://github.com/FortAwesome/vue-fontawesome#introduction) — I use these as social icons.
- `@gridsome/plugin-google-analytics` ([link](https://gridsome.org/plugins/@gridsome/plugin-google-analytics)) — Google Analytics because I want to track how many people come and go.
- `@gridsome/plugin-sitemap` ([link](https://gridsome.org/plugins/@gridsome/plugin-sitemap)) — 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](https://gridsome.org/plugins/@gridsome/remark-prismjs)) — For code highlighting.
- `@gridsome/source-filesystem` ([link](https://gridsome.org/plugins/@gridsome/source-filesystem)) — 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](https://gridsome.org/plugins/@gridsome/transformer-remark)) — This is there so the markdown can be translated into HTML.
- `gridsome-plugin-netlify-cms` ([link](https://gridsome.org/plugins/gridsome-plugin-netlify-cms)) — I use this to make it easier to manage all the markdown files for my posts.
    - `netlify-cms` ([link](https://www.netlifycms.org)) — See above
- `gridsome-remark-katex` ([link](https://gridsome.org/plugins/gridsome-remark-katex)) — I wanted to be able to write math equations on my website that aren't ugly: $e=mc^2$ compared to e = mc^2.
    - `remark-html` ([link](https://github.com/remarkjs/remark-html)) — See above
    - `remark-html-katex` ([link](https://www.npmjs.com/package/remark-html-katex)) — See above
    - `remark-math` ([link](https://github.com/remarkjs/remark-math)) — See above
- `gridsome-plugin-tailwindcss` ([link](https://gridsome.org/plugins/gridsome-plugin-tailwindcss)) — 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:

```json
"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](https://gridsome.org/plugins/@gridsome/source-filesystem)) plugin where to look.

Just follow the instructions to set it up on the [plugin page](https://www.notion.so/How-I-Made-a-Personal-Blog-with-Vue-Gridsome-d43398eab4304146bedbc6735df825c8#12667f733dac412bb5e3fcf4931365ff). 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.

```jsx
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](https://github.com/PrismJS/prism-themes).

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

```jsx
head.link.push({
    rel: 'stylesheet',
    href: 'https://cdn.jsdelivr.net/npm/prism-themes@1.4.0/themes/prism-xonokai.css'
  })
  head.link.push({
    rel: 'stylesheet',
    href: 'https://cdn.jsdelivr.net/npm/katex@0.10.2/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](https://gridsome.org/plugins/gridsome-plugin-netlify-cms)) page. And then the [Netlify CMS guide](https://gridsome.org/docs/guide-netlify-cms/) 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:

```jsx
{
      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:

```jsx
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](https://gridsome.org/docs/templates/) has a great introduction to making these.

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

```jsx
<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:

```jsx
<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](https://www.netlify.com). 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](https://gridsome.org/docs/deploy-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](https://bagerbach.com).

Slowly, I've been learning more about GraphQL and Vue by creating this site. Everything I've learned past is a bit outside the scope of this post.

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.