This is part twelve of a series on building a file-based blog from scratch with Gridsome. Find the full series here.
SEO is crucial. And with site speed being a big factor in SEO, we are set up for a good start with Gridsome.
Great technical SEO is the basis for all other SEO techniques. Here are the basics:
Gridsome is already great at site render speed and your overall Lighthouse score. But it's always worth to check yourself. Make sure your site is accessible, looks great on mobile, and that you optimize your images.
To get the site download speed and HTTPS right, I strongly recommend to use a service like Netlify, Vercel, or GitHub Pages+CloudFlare.
We're now going to focus on the last three items: having a robots.txt
, providing a sitemap.xml
, and using all necessary meta
tags.
This is the easiest part.
Add a file called static/robots.txt
with the following contents:
Sitemap: https://mannes.tech/sitemap.xml
User-agent:*
Disallow: /legal
Disallow: /privacy
We use the robots.txt
file to tell web crawlers what we allow them to index on our site. Find more about robots.txt
on Neil Patel's blog.
A thought for EU-based developers: you can set your legal notice and privacy policy to Disallow
.
It only needs to be visible to your website visitors.
When you disallow these pages via robots.txt, you can prevent Google from indexing these sites if you also add a noindex meta tag to the header of those pages.
A sitemap is an xml file that contains all URLs on your website. Search engines use this information when crawling your website.
Adding a sitemap is straightforward.
First, add the sitemap plugin with yarn add @gridsome/plugin-sitemap
.
Then, open your gridsome.config.js
and use it like this:
// beginning of file
module.exports = {
siteName: 'Gridsome',
plugins: [
// your current plugins
{
use: '@gridsome/plugin-sitemap',
options: {
exclude: ['/privacy', '/legal']
}
}
],
// rest of gridsome config
}
You can find all documented options for the sitemap plugin here.
Meta tags tell search engines and social networks how to display your site on their page. They also give additional information like which Twitter card format should be used, which image should be rendered, and what your Twitter handle is.
First, we want to add some additional site information in our gridsome.config.js
:
// beginning of file
module.exports = {
siteName: 'Awesome Blog',
siteDescription: 'Blog about awesome lists, collections of resources around a specific technology.',
siteUrl: process.env.DEPLOY_URL || 'https://blog.awesome',
metadata: {
twitter: {
site: '@simon_mannes',
creator: '@simon_mannes'
}
},
// rest of file
}
Next, we use these definitions in our default layout to set some general meta tags.
Edit your src/layouts/Default.vue
file:
<template>
<!-- redacted for readability -->
</template>
<static-query>
query {
metadata {
siteName
siteDescription
siteUrl
author
twitter {
site
creator
}
}
}
</static-query>
<script>
// ...
export default {
// ...
metaInfo () {
return {
meta: [
{ key: 'author', name: 'author', content: this.$static.metadata.author },
{ key: 'twitter:site', name: 'twitter:site', content: this.$static.metadata.twitter.site },
{ key: 'twitter:creator', name: 'twitter:creator', content: this.$static.metadata.twitter.creator }
]
}
}
}
</script>
Now, we create an SEO component that we can use as a mixin
in our posts.
Add a file called src/mixins/SEO.vue
:
<static-query>
query {
metadata {
siteName
siteDescription
siteUrl
author
}
}
</static-query>
<script>
export default {
metaInfo () {
const siteUrl = this.$static.metadata.siteUrl
const postPath = this.$page.post.path
const image = this.$page.post.image?.path
const imagePath = (image && `${siteUrl}${image.src}`) || ''
return {
title: this.$page.post.title,
meta: [
{ key: 'description', name: 'description', content: this.$page.post.summary },
{ key: 'og:url', property: 'og:url', content: `${siteUrl}${postPath}` },
{
key: 'og:title',
property: 'og:title',
content: this.$page.post.title
},
{
key: 'og:type',
property: 'og:type',
content: 'article'
},
{
key: 'og:description',
property: 'og:description',
content: this.$page.post.summary
},
{
key: 'og:image',
property: 'og:image',
content: imagePath
},
{
key: 'og:image:width',
property: 'og:image:width',
content: (image && image.size.width) || ''
},
{
key: 'og:image:height',
property: 'og:image:height',
content: (image && image.size.height) || ''
},
{
key: 'twitter:description',
name: 'twitter:description',
content: this.$page.post.summary
},
{
key: 'twitter:card',
name: 'twitter:card',
content: image ? 'summary_large_image' : 'summary'
},
{
key: 'twitter:image',
property: 'twitter:image',
content: imagePath
},
{
key: 'twitter:title',
property: 'twitter:title',
content: this.$page.post.title
}
],
script: [
{
type: 'application/ld+json',
json: {
'@context': 'http://schema.org',
'@type': 'BlogPosting',
description: this.$page.post.description,
datePublished: this.$page.post.date,
author: {
name: this.$static.metadata.author
},
headline: this.$page.post.title,
image: imagePath
}
}
]
}
}
}
</script>
In this file, we set the necessary meta:
title
and description
og
tags (OpenGraph) as information for social networkstwitter
tags specifically for twitter, including the necessary definitions for twitter image cardsNote that we will add cover images in the next post and they will not be used at the moment.
Finally, add this mixin to your src/templates/BlogPost.vue
, remove the old metaInfo
property, and add path
and summary
to the page-query
:
<template>
<!-- redacted for readability -->
</template>
<page-query>
query Post ($path: String!) {
post: blogPost (path: $path) {
title
date (format: "MMMM D, Y")
content
path
summary
tags {
title
path
}
}
}
</page-query>
<script>
import PostSeo from '../mixins/Postseo'
export default {
mixins: [PostSeo]
}
</script>
<style src="../css/markdown.css" />
But before we can use the summary
field as our description meta tag, we need to add it to the front matter of our posts.
Extend the front matter of your blog posts with a summary
property like this:
---
slug: "third-post"
date: "2020-07-03"
title: "Code Coloring"
tags: ["frontend", "design", "code style"]
summary: "Banh mi authentic fashion axe affogato shoreditch umami bicycle rights keytar put a bird on it drinking vinegar pitchfork taxidermy."
---
## This should be a heading 2
I'm baby chartreuse knausgaard gastropub deep v mlkshk pickled crucifix chicharrones meggings.
...
When we now build our Gridsome project these are our meta tags on the blog post page for the Hipster Ipsum
article:
<!DOCTYPE html>
<html data-html-server-rendered="true" lang="en" data-vue-tag="%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D">
<head>
<title>Hipster Ipsum - Awesome Blog</title>
<meta name="gridsome:hash" content="68f4394ecd32124a83710bacd50fdcb1466dd174">
<meta data-vue-tag="ssr" charset="utf-8">
<meta data-vue-tag="ssr" name="generator" content="Gridsome v0.7.15">
<meta data-vue-tag="ssr" data-key="viewport" name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta data-vue-tag="ssr" data-key="format-detection" name="format-detection" content="telephone=no">
<meta data-vue-tag="ssr" data-key="description" name="description" content="Listicle jianbing tbh sriracha tofu, waistcoat post-ironic copper mug williamsburg scenester.">
<meta data-vue-tag="ssr" data-key="og:url" property="og:url" content="https://blog.awesome/blog/first-post/">
<meta data-vue-tag="ssr" data-key="og:title" property="og:title" content="Hipster Ipsum">
<meta data-vue-tag="ssr" data-key="og:type" property="og:type" content="article">
<meta data-vue-tag="ssr" data-key="og:description" property="og:description" content="Listicle jianbing tbh sriracha tofu, waistcoat post-ironic copper mug williamsburg scenester.">
<meta data-vue-tag="ssr" data-key="og:image" property="og:image" content="">
<meta data-vue-tag="ssr" data-key="og:image:width" property="og:image:width" content="">
<meta data-vue-tag="ssr" data-key="og:image:height" property="og:image:height" content="">
<meta data-vue-tag="ssr" data-key="twitter:description" name="twitter:description" content="Listicle jianbing tbh sriracha tofu, waistcoat post-ironic copper mug williamsburg scenester.">
<meta data-vue-tag="ssr" data-key="twitter:card" name="twitter:card" content="summary">
<meta data-vue-tag="ssr" data-key="twitter:image" property="twitter:image" content="">
<meta data-vue-tag="ssr" data-key="twitter:title" property="twitter:title" content="Hipster Ipsum">
<meta data-vue-tag="ssr" data-key="author" name="author" content="Simon Mannes">
<meta data-vue-tag="ssr" data-key="twitter:site" name="twitter:site" content="@simon_mannes">
<meta data-vue-tag="ssr" data-key="twitter:creator" name="twitter:creator" content="@simon_mannes">
<!-- ... -->
<script data-vue-tag="ssr" type="application/ld+json">{"@context":"http://schema.org","@type":"BlogPosting","datePublished":"June 5, 2020","author":{"name":"Simon Mannes"},"headline":"Hipster Ipsum","image":""}</script>
<!-- ... -->
When you deploy your blog (e.g. with Netlify) you can use one of the available meta validation tools to check how Google or Twitter render your site:
That’s it for this post on how to improve SEO on your Gridsome blog. In the next part of this series, we add cover images to our blog posts.