Preface
Goal: Flexible SEO with Hugo, w3c, opengraph and twitter.
Don’t you love a preview on your site? I mean, that you can customize differently, based on the content.
You can see at above figure, each post has different image.
1: Prepare
There are few artefacts changes.
Artefacts
Edit
-
config.toml.
-
themes/tutor-06/layouts/partials/site-head.html.
New
-
themes/tutor-06/layouts/partials/meta-opengraph.html.
-
themes/tutor-06/layouts/partials/meta-twitter.html.
Example Frontmatter
- content/quotes/john-mayer-slow-dancing-in-a-burning-room.md.
2: Refactoring: Head Tag
Our opengraph meta, lies inside head tag.
Layout: Head
To avoid complicated code, we are going to us partial for opengraph. Since we are going to use partial for opengraph, we should have a look at our last site-head, and add this two lines.
<head>
{{ partial "meta-html.html" . }}
...
{{ partial "meta-seo.html" . }}
{{ partial "meta-opengraph.html" . }}
{{ partial "meta-twitter.html" . }}
</head>
Layout: Enhanced Head
For a more complete code, I also provice an enhanced version of site-head. My intention is to make a more complete example for you. I also put some comment, so we know what each part does.
- themes/tutor-06/layouts/partials/site-head.html.
<head>
{{ partial "meta-html.html" . }}
{{/* NOTE: the Site's title, and if there is a page title, that is set too */}}
<title>{{ .Page.Title | default .Site.Title }}</title>
{{/* Bootstrap CSS */}}
<link rel="stylesheet" type="text/css" href="{{ "css/bootstrap.css" | relURL }}">
<link rel="stylesheet" type="text/css" href="{{ "css/main.css" | relURL }}">
<link rel="stylesheet" type="text/css" href="{{ "css/prism.css" | relURL }}">
{{ block "custom-stylesheet" . }}{{ end }}
{{/* javascript */}}
{{/* miscellanous */}}
<link href="{{ "favicon.ico" | relURL }}" rel="shortcut icon" type="image/x-icon" />
{{ partial "meta-seo.html" . }}
{{ partial "meta-opengraph.html" . }}
{{ partial "meta-twitter.html" . }}
{{/* unused */}}
</head>
SEO: HTML Preview
Don’t you love that your site is ready for Search Engine Optimization ? The HTML that we want to achieve is similar as example page below:
<head>
...
<meta name="author" content="epsi">
<meta name="description" content="Shiny and sparkly. And splendidly bright. Here one day. Gone one night. ">
<meta name="keywords" content="lyric, pop, 90s">
<meta property="og:locale" content="en-us">
<meta property="og:type" content="article">
<meta property="og:title" content="Michael Jackson - Gone Too Soon">
<meta property="og:description" content="Shiny and sparkly. And splendidly bright. Here one day. Gone one night. ">
<meta property="og:url" content="http://localhost:1313/quotes/2015/05/15/michael-jackson-gone-too-soon/">
<meta property="og:site_name" content="Letters to my Beloved">
<meta property="og:image" content="http://localhost:1313/images/epsi-vexel.png">
<meta property="og:latitude" content="-6.193665"/>
<meta property="og:longitude" content="106.848558"/>
<meta property="og:locality" content="Jakarta"/>
<meta property="og:country-name" content="Indonesia"/>
<meta name="twitter:title" content="Michael Jackson - Gone Too Soon">
<meta name="twitter:description" content="Shiny and sparkly. And splendidly bright. Here one day. Gone one night. ">
...
</head>
3: Meta HTML
Consider move important meta to special meta-html.html. This should be an easy task, since it is only HTML.
Layout: Meta HTML
Remember that, some of these metas must come first in the head tag.
- themes/tutor-06/layouts/partials/meta-html.html.
{{/* Required meta tags */}}
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{/* The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags */}}
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
Optionally, you can add any meta that you need.
{{/* Let's Fake the Generator */}}
<meta name="generator" content="Karimata 1.2.3 Media Engine" />
{{/* Chrome, Firefox OS and Opera */}}
<meta name="theme-color" content="#2980b9">
4: SEO: W3C
The next step is, put valid w3c SEO related meta.
Layout: Meta SEO
- themes/tutor-06/layouts/partials/meta-seo.html.
{{- if .Page.Params.Author }}
<meta name="author" content="{{ .Page.Params.Author }}">
{{- end }}
{{- if .Page.Params.Excerpt }}
<meta name="description" content="{{ replace .Page.Params.Excerpt "\n" " " }}">
{{- end }}
{{- $terms := union .Page.Params.Categories .Page.Params.Tags -}}
{{- if $terms }}
<meta name="keywords" content="{{ delimit $terms ", " }}">
{{- end }}
Content: Example
If you need, you can experiment with excerpt parameters in frontmatter.
+++
type = "post"
title = "Michael Jackson - Gone Too Soon"
date = 2015-05-15T07:35:05+07:00
categories = ["lyric"]
tags = ["pop", "90s"]
slug = "michael-jackson-gone-too-soon"
author = "epsi"
excerpt = """\
Shiny and sparkly.
And splendidly bright.
Here one day.
Gone one night.
"""
+++
Shiny and sparkly.
And splendidly bright.
Here one day.
Gone one night.
This will result as below:
<meta name="description" content="Shiny and sparkly. And splendidly bright. Here one day. Gone one night. ">
How Does Keywords Works?
Merge array is easy in Hugo, using union:
{{- $terms := union .Page.Params.Categories .Page.Params.Tags -}}
Join the array into one string require delimit function.
{{ delimit $terms ", " }}
This will result as below:
<meta name="keywords" content="lyric, pop, 90s">
5: SEO: Opengraph
The Structure of opengraph can be read from official page:
HTML Preview
The HTML that we want to achieve is similar as below.
...
<meta property="og:title" content="John Mayer - Slow Dancing in a Burning Room">
<meta property="og:url" content="http://localhost:1313/quotes/2018/02/15/john-mayer-slow-dancing-in-a-burning-room/">
<meta property="og:site_name" content="Letters to my Beloved">
<meta property="og:image" content="http://localhost:1313/assets/posts/2018/kiddo-007.jpg">
...
Structure
How to achieve ?
There are three parts:
-
Common properties: using conditional.
-
Image: using default image, depend on page type.
-
Location: Hardcoded.
Partial: Meta Opengraph
This would takes some conditional, but it self explanatory.
<meta property="og:locale" content="{{ $.Site.LanguageCode | default "en" }}">
{{- if eq .Page.Params.Type "post" }}
<meta property="og:type" content="article">
{{- end }}
<meta property="og:title" content="{{ .Title | default .Site.Title }}">
{{- if .Page.Params.Excerpt }}
<meta property="og:description" content="{{ replace .Page.Params.Excerpt "\n" " " }}">
{{- end }}
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
{{- if .Page.Params.Opengraph.Image }}
<meta property="og:image" content="{{ .Page.Params.Opengraph.Image | absURL }}">
{{- else if eq .Page.Params.Type "post" }}
<meta property="og:image" content="{{ "/images/epsi-vexel.png" | absURL }}">
{{- else if .Site.Params.Opengraphimage }}
<meta property="og:image" content="{{ .Site.Params.Opengraphimage | absURL }}">
{{- end }}
<meta property="og:latitude" content="-6.193665"/>
<meta property="og:longitude" content="106.848558"/>
<meta property="og:locality" content="Jakarta"/>
<meta property="og:country-name" content="Indonesia"/>
Default Image
Now we have three possibility
-
General: set in config.toml
-
Post Type: hardcoded as /assets/site/images/epsi-vexel.png
-
Set in frontmatter.
Generic Image
We can set parameters in config.toml, as below:
[params]
description = """\
Learn and Discover Open Source with Daily Genuine Experience.
From Coding, Front End, Back End, Database, and Static Site Generator.
"""
opengraphimage = "assets/site/images/logo-gear-opengraph.png"
For any pages, except post, we can use the opengraphimage parameter above.
Default Image for Post
Since it is just an example. I apologize for my hardcoded example. You can code whatever you like, rather than just hardcoded example.
Frontmatter Example
And in any post, we can set, as example below:
- content/quotes/john-mayer-slow-dancing-in-a-burning-room.md.
type = "post"
title = "John Mayer - Slow Dancing in a Burning Room"
date = 2018-02-15T07:35:05+07:00
categories = ["lyric"]
tags = ["rock", "2010s"]
slug = "john-mayer-slow-dancing-in-a-burning-room"
author = "epsi"
[opengraph]
image = "assets/posts/2018/kiddo-007.jpg"
6: SEO: Twitter
The twitter version is much more simple. Because, I do not really pay attention to twitter.
<meta name="twitter:title" content="{{ .Title | default .Site.Title }}">
{{- if .Page.Params.Excerpt }}
<meta name="twitter:description" content="{{ replace .Page.Params.Excerpt "\n" " " }}">
{{- end }}
{{- if .Site.Data.owner.twitter }}
<meta name="twitter:site" content="@{{ .Site.Data.owner.twitter }}">
{{- end }}
Summary
I think that is all. Short and simple.
What is Next ?
There are, some interesting topic about using Service in Hugo, such as inserting Google Analytics and Disqus Comments. Consider continue reading [ Hugo - Service ].
Thank you for reading.