Originally published at Codegram's blog
At Codegram we are already pretty sold on the JAMStack, especially using Gridsome, a Vue.js framework (for React devs, it would equivalent to Gatsby). Despite being just version v0.6.9
at the time I'm writing this, it's pretty much production-ready (unless you need i18n), and offers a great developer experience. We've built a few sites with it, most notably Full Stack Fest's and the very own where you are reading this.
Where Gridsome shines the most versus, let's say, a simple statically generated Nuxt site, is the ability to add a GraphQL data layer with just a few lines of code and 0 experience creating APIs. The first and most obvious need our website had that could be solved with this was the blog. So let's see how to create a blog with Gridsome!
First, we will need to install a couple of plugins: @gridsome/source-filesystem and @gridsome/transformer-remark. This will allow us to read markdown files and turn those into a GraphQL collection.
So, in gridsome.config.js
file, we add the configuration for the plugin:
module.exports = {
plugins: [
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/blog/*.md',
typeName: 'BlogPost',
route: '/blog/:slug'
}
}
],
transformers: {
remark: {
// we can add options here
}
}
}
Then, we create a blog.vue
file inside src/pages
, and we can query our blogposts. It's that easy!
<template>
<Layout>
<BlogPost
v-for="post in $page.blogPosts.edges"
:key="post.node.id"
:post="post"
/>
</Layout>
</template>
<static-query>
query BlogPost {
blogPosts: allBlogPost(sortBy: "published_at", order: DESC) {
edges {
node {
title
path
image(width:640, quality: 75)
}
}
}
}
</static-query>
With the power of GraphQL, we only need to query the fields we need. Since it's the main blog page, we don't care about the content, so we only request title
, path
, and image
(which we can even request in specific size and quality! 🤯).
What about each post page? With plain Vue or Nuxt we would need to create a route that with an id
param, then get that param from the $route
object, then query the post, set the data we get to the component state, probably adding a loading spinner while doing all that…
With Gridsome is much easier! We only need to create a BlogPost.vue
in the templates
folder (remember that BlogPost
is the typeName
we used when configuring our app), and write the query:
<template>
<section>
<h1>{{ $page.blogPost.title }}</h1>
<!-- ... -->
</section>
</template>
<page-query>
query BlogPost ($path: String!) {
blogPost: blogPost (path: $path) {
title
published_at
author
image(width:1024, quality: 75)
content
}
}
</page-query>
As you see, the query gets a $path
variable, that we pass along. But all that is done for you, no need to get the $route.params.id
or anything!
That's only the beginning of what you can do with Gridsome, though. What good is a blog without tags, you might ask? Let's add them!
We need another GraphQL collection, but we don't want to have to create a file for each tag, right? Luckily there's an easy way! In gridsome.config.js
we can add a reference to another typeName
. The create
option will create the collection without files:
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/blog/*.md',
typeName: 'BlogPost',
route: '/blog/:slug',
refs: {
typeName: 'Tag',
route: '/blog/tag/:slug',
create: true
}
}
}
Now we can query the tags the same way to create our tag list:
<page-query>
query Tags {
tags: allTag {
edges {
node {
id
path
}
}
}
}
</page-query>
But we will need to create a view for each tag, that lists the posts that have that tag, right? We create another template Tag.vue
, and we can get the posts that the tag belongs to!
<page-query>
query Tag($id: String!) {
tag(id: $id) {
id
belongsTo(sortBy: "published_at", order: DESC) {
edges {
node {
... on BlogPost {
title
path
image(width:640, quality: 75)
}
}
}
}
}
}
</page-query>
That ... on BlogPost
bit might look a bit weird if you are not familiar with GraphQL, it's called an inline fragment.
Isn't it great to be able to create relationships that easily? Without a backend! To access the posts easily, I like to create computed properties so the code is more readable:
computed: {
posts() {
return this.$page.tag.belongsTo.edges
}
}
This might not feel as much, but this truly brings a static site to the next level. We can create complex relationships between all the pieces that form our website. For example, we added related posts to some of our services. How? Adding tags as well!
// gridsome.config.js
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/services/*.json',
route: '/services/:id',
typeName: 'Service',
refs: {
tags: {
typeName: 'Tag',
route: '/blog/tag/:slug'
}
}
}
},
Since we already created the tags for our blog posts, there is no need to add the create: true
again, it will match against current existing tags.
On our services query, we add the tags and their posts:
<page-query>
query Service($path: String!) {
service(path: $path) {
title
description
tags {
id
belongsTo(
sortBy: "published_at",
order: DESC,
limit: 3,
filter: {
typeName: { eq: BlogPost }
}) {
edges {
node {
... on BlogPost {
title
path
image(width: 640, quality: 75)
}
}
}
}
}
}
}
</page-query>
Note that since now tags have references to both BlogPosts and Services, we need to filter by typeName
so we only get posts.
That easily, every time we add a post about Vue, it will show up on the Vue service page. And it works the other way around too! We can query related services from the post page, as you can see at the end of this post. And we did the same with case studies! If you read a post about Decidim, you will see a link to the case study. Post tags enabled us to display relationships throughout the site.
Imagine the possibilities
As we've seen, the JAMStack is not limited to simple sites with a few pages, or blogs at the most. We can create complex relationships and filters very easily, so we can sort our products in multiple ways (categories, families, tags, etc), add related products, link them to a different content type… you name it! In this post, we've been working with a file loader but Gridsome supports multiple sources: you can use popular headless CMS like Contentful or Netlify CMS, Wordpress, or even plugging it directly to a MySQL database. So even if your site has a lot of content, it's easily manageable with JAMStack too.
Apparently, you can even use the Star Wars API as a source. So try Gridsome today, it is the framework you are looking for.