Some Notes on Building Blogs with Gridsome

Some Notes on Building Blogs with Gridsome

I’m doing some research for an upcoming presentation on Gridsome (An Introduction to Gridsome on January 22) and ran into an issue understanding how one particular feature works that is of particular interest to folks building blogs with Gridsome.

tl;dr

The frontmatter of your Markdown files become part of your GraphQL schema. You must use date as a field if you want URLs (paths) based on a date property. You will get additional properties (like excerpt) automatically.

Gridsome supports a filesystem plugin that let you point at a folder of files and automatically import them for use within your site. This is first shown in the docs for Import with source plugins. The specific code example show is this:

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'docs/**/*.md',
        typeName: 'DocPage'
      }
    }
  ]
}

That’s fairly straight forward. Point to a path, give it a type name that will be used for the GraphQL server and you’re good to go. You need a bit more information though, and you can then checkout the docs for source-filesystem. This page brings up an important detail - that in order to use the filesystem a transformer must be used to parse your files. For Markdown, you are asked to install @gridsome/transformer-remark.

So far so good, but let’s look at the sample code used in the plugin doc:

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        typeName: 'BlogPost',
        path: './content/blog/**/*.md',
      }
    }
  ],
  templates: {
    BlogPost: '/blog/:year/:month/:day/:slug'
  }
}

One important detail different is that this example shows that you need a template to handle displaying the blog posts. Templates are how Gridsome transform collections of data into pages. Personally I don’t like the name as templates make me think of layouts, but that’s just me. ;)

Ok…. so here’s where I began to have issues. If you look at the template defintion above, you’ll see this value: /blog/:year/:month/:day/:slug. While I admit I didn’t read every single line of Gridsome documentation, I wasn’t sure where slug, year, month, and day came from.

To make matters worse, when I tested, I used one Markdown file that looked like this:

Hi I'm *markdown*!

That’s a valid example, but it was missing a crucial part, frontmatter.

So while it’s mostly obvious, I guess, that a blog using Markdown files will use frontmatter, it wasn’t necessarily called out how important this was.

Gridsome, the filesystem plugin, and the transformer plugin beneath that, will make use of your frontmatter in multiple ways.

First, all the unique values of your frontmatter will be made available in the GraphQL collection. What do I mean by that? Imagine your blog has two files:

```
title: Blog one
cat: I like cats
```

And…

```
title: Blog one
dog: I like cats too
```

The resultant GraphQL schema type will include: title, cat, and dog properties. But that’s not all - I’ll get back to the full schema later in the post.

Alright, so what about :slug? A slug generally refers to taking a string and making URL safe (and lower case), so something like “Ray’s Happy World” turns into “rays-happy-world”. When I used :slug in my testing, I got this error:

Notice about using slug versus title

Ok… so… I guess if my frontmatter included slug then I’d be fine, but for now I simply switched to using :title and it used that value from my frontmatter.

Alright, so what about :year/:month/:day? In my testing this only worked if your frontmatter uses a value called date and it follows the UTC date format, then it will be picked up and parsed automatically. So like so:

---
title: Goo
date: 2020-01-05
---

This is goo

This will output a URL path like so, /2020/01/05. I didn’t find this till this morning, but it is documented on the templates page.

Unfortunately I can’t see anyway to change this behavior if you use another field for your date. To be clear, if you use another date field, like edited: 2020-01-10 then Gridsome (and the relevant plugins) will recognize it as a date and make it available as a date type in GraphQL, but I don’t believe you can use it in the template path. Wait, I lie. It absolutely can be if you write a custom path function. Here’s how the templates doc demonstrate that:

// gridsome.config.js
module.exports = {
  templates: {
    Post: [
      {
        path: (node) => {
          return `/product/${node.slug}/reviews`
        }
      }
    ]
  }
}

So in my case I could use values from node.edited.

Finally, if for some reason you don’t specify a date field in your frontmatter, the output will include “Invalid date” in the path: http://localhost:8080/blog/Invalid%20date/Invalid%20date/Invalid%20date/ray-rules/ So don’t do that. ;)

Ok… there’s more. When Gridsome (and the relevant plugins again) parse your frontmatter, you get a lot of fields in your collection. For a test, I used three Markdown files with frontmatter like this:

---
title: dude
date: 2020-01-05
cover_image: goo.jpg
tags: php, perl
---

This generated this GraphQL schema:

type BlogPost implements Node {
  id: ID!
  path(to: String = "default"): String
  fileInfo: BlogPost_FileInfo
  content: String
  excerpt: String
  title: String
  date(
    format: String
    locale: String
  ): Date
  cover_image: String
  tags: String
  headings(
    depth: RemarkHeadingLevels
    stripTags: Boolean = true
  ): [RemarkHeading]
  timeToRead(speed: Int = 230): Int
  belongsTo(
    sortBy: String = "date"
    order: SortOrder = DESC
    perPage: Int
    skip: Int = 0
    limit: Int
    page: Int
    sort: [SortArgument!]
    filter: BelongsToFilterInput
  ): NodeBelongsTo
}

There’s a lot there to note. For example, excerpt is useful for sure and timeToRead is nice. I’m guessing these come from the transformer, but I’m not sure where this is documented. excerpt is documented, I believe, way down in the gray-matter code which is a couple chains down from the top plugin, but I really wish this was closer to the “main” Gridsome docs if that makes sense. headings seems to come from the fact that the transformer plugin supports automatically creating anchors for your headings in Markdown. I mean I assume that’s why. I like the automatic anchors. Not sure when I’d use it in GraphQL query. (Oh, I know how - for maybe making a table of contents for a long documentation page.)

You’ll notice that my tags ended up as a string. The source-filesystem plugin supports a refs feature that lets you fix that:

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        refs: {
          // Create a Tag content type and its nodes automatically.
          tags: {
            typeName: 'Tag',
            create: true
          }
        }
      }
    }
  ]
}

I ran into one more issue though. It treated my multitag value “php, perl” as a string. When I rewrote it like so, tags: ["php","perl"] it worked. I also saw this represented in the GraphQL schema:

tags(
    sortBy: String
    order: SortOrder = DESC
    skip: Int = 0
    sort: [SortArgument]
    limit: Int
  ): [Tag]

Finally, and I think I’m really done now, sometimes it took a server restart for Gridsome to notice and update the GraphQL schema. As a specific example, when I was testing getting tags to work as an array I had to restart with every change. I hope this helps!

Header photo by Patrick Hendry on Unsplash

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Comments