Hugo - customized Open Graph integration

Open Graph is a protocol that can be used to influence how a web page is displayed when it is shared on social media. Via meta entries in the head of each web page you can influence which data is used for this. In this article I describe my Open Graph meta tags, which are summarized in a partial.
General information about Open Graph
Originally, Open Graph was developed by Facebook. However, it has since been adopted by other social media channels such as Twitter, Pinterest, and LinkedIn.
Most content is shared as a URL on Facebook and other (Open Graph Protocol) OGP-supporting websites. For this reason, it can only be positive if one’s website is equipped with Open Graph meta tags. This is the only way to have control over what content is displayed there.
Since I want to understand what leads to a corresponding output in the source code and what possibilities there are, I looked at the following websites, among others:
Hugo Open Graph Template
Hugo provides a template for the integration of the Open Graph meta tags. The template is called in the head and integrates the meta tags. If you want to use the template, the following call is sufficient:
{{ template "_internal/opengraph.html" . }}
For a better understanding of what the template does, there is a Hugo doc and a link to the template source code:
My Partial ogData.html
The Hugo template is designed for many purposes. Since I only want to output the content adapted for my website, I have taken over or rewritten parts of the template. More about this below.
I included the partial ogData.html in the head
tag of my baseof.html
as follows:
{{ partial "ogData" . }}
The source code of the partial themes/tekki/layouts/partials/ogData.html
looks like this:
<meta property="og:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }}">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
<meta property="og:description" content="{{ if .Description }}{{ .Description }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
<meta property="og:url" content="{{ .Permalink }}">
{{ if eq .Site.Language.Lang "de" }}
<meta property="og:locale" content="de_DE">
<meta property="og:locale:alternate" content="en_GB">
{{ else }}
<meta property="og:locale" content="en_GB">
<meta property="og:locale:alternate" content="de_DE">
{{ end }}
{{ if and (.IsPage) (ne .Section "") }}<meta property="article:section" content="{{ .Section }}">{{ end }}
{{ $iso8601 := "2006-01-02T15:04:05-07:00" }}
{{ with .PublishDate }}<meta property="article:published_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }}>{{ end }}
{{ with .Lastmod }}<meta property="article:modified_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }}>{{ end }}
{{ $image := .Resources.GetMatch "featured" }}
{{ with $image }}
<meta property="og:image" content="{{ .Permalink }}">
<meta property="og:image:secure_url" content="{{ .Permalink }}">
<meta property="og:image:type" content="{{ .MediaType }}">
<meta property="og:image:width" content="{{ .Width }}">
<meta property="og:image:height" content="{{ .Height }}">
<meta property="og:image:alt" content="{{ .Title }}">
{{ else }}
{{ $ogimage := resources.Get "tekki-tipps-og.png" }}
{{ with $ogimage }}
<meta property="og:image" content="{{ .Permalink }}">
<meta property="og:image:secure_url" content="{{ .Permalink }}">
<meta property="og:image:type" content="{{ .MediaType }}">
<meta property="og:image:width" content="{{ .Width }}">
<meta property="og:image:height" content="{{ .Height }}">
<meta property="og:image:alt" content="Blog about Hugo, web design, CSS/SCSS, SEO, Tools">
{{ end}}
{{ end }}
For better understanding I will describe each meta tag individually.
meta property=“og:title”
<meta property="og:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }}">
If the current web page is the front page, the title
is taken from the hugo.toml
. Otherwise, for all other web pages, the title
is taken from the Front Matter entry of the current web page. A hyphen is inserted and the title
from the hugo.toml
is appended.
The entries of the hugo.toml
for German and English look like this:
[languages]
[languages.de]
languageName = "Deutsch"
weight = 1
title = "tekki-tipps.de π©πͺ"
description = "Blog ΓΌber Hugo, Webdesign, CSS/SCSS, SEO, Tools"
..
..
[languages.en]
languageName = "English"
weight = 2
title = "tekki-tipps.de/en π¬π§"
description = "Blog about Hugo, web design, CSS/SCSS, SEO, Tools"
meta property=“og:type”
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
On my website the og:type website
is displayed for the home page, the tag cloud and on tag lists. So on all overview lists. All other web pages get the og:type article
.
meta property=“og:description”
<meta property="og:description" content="{{ if .Description }}{{ .Description }}{{ else }}{{ .Site.Params.description }}{{ end }}">
If the current web page in Front Matter has a description
, this is taken over by .Description
. Otherwise the description
of the hugo.toml
is used.
meta property=“og:site_name”
<meta property="og:site_name" content="{{ .Site.Title }}">
The title from the hugo.toml
.
meta property=“og:url”
<meta property="og:url" content="{{ .Permalink }}">
The permanent URL of the current web page.
meta property=“og:locale” und “og:locale:alternate”
{{ if eq .Site.Language.Lang "de" }}
<meta property="og:locale" content="de_DE">
<meta property="og:locale:alternate" content="en_GB">
{{ else }}
<meta property="og:locale" content="en_GB">
<meta property="og:locale:alternate" content="de_DE">
{{ end }}
og:locale
defines the language of the current web page. og:locale:alternate
tells that there is a translation in an alternate language for the current web page.
meta property=“article:section”
{{ if and (.IsPage) (ne .Section "") }}<meta property="article:section" content="{{ .Section }}">{{ end }}
If the current web page is a Page and the .Section
is not an empty string, then the meta tag should be written in the Head. On my website there is only the section blog
. To prevent the meta tag from being written to the head for web pages that do not have a section, a query is made to see if the string in .Section
is empty.
meta property=“article:published_time” and article:modified_time
{{ $iso8601 := "2006-01-02T15:04:05-07:00" }}
{{ with .PublishDate }}<meta property="article:published_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }}>{{ end }}
{{ with .Lastmod }}<meta property="article:modified_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }}>{{ end }}
The overview pages - home page, tag cloud and tag lists - do not have .PublishDate
hence the query. All other web pages may not have .Lastmod
yet.
meta property=“og:image”
{{ $image := .Resources.GetMatch "featured" }}
{{ with $image }}
<meta property="og:image" content="{{ .Permalink }}">
<meta property="og:image:secure_url" content="{{ .Permalink }}">
<meta property="og:image:type" content="{{ .MediaType }}">
<meta property="og:image:width" content="{{ .Width }}">
<meta property="og:image:height" content="{{ .Height }}">
<meta property="og:image:alt" content="{{ .Title }}">
{{ else }}
{{ $ogimage := resources.Get "tekki-tipps-og.png" }}
{{ with $ogimage }}
<meta property="og:image" content="{{ .Permalink }}">
<meta property="og:image:secure_url" content="{{ .Permalink }}">
<meta property="og:image:type" content="{{ .MediaType }}">
<meta property="og:image:width" content="{{ .Width }}">
<meta property="og:image:height" content="{{ .Height }}">
<meta property="og:image:alt" content="Blog about Hugo, web design, CSS/SCSS, SEO, Tools">
{{ end}}
{{ end }}
Now it gets a little more complicated. Therefore I have to explain a bit more. My web pages are multilingual in two languages. I use PageBundles because of the clarity - everything for one web page, in one directory. The respective images are stored inside the PageBundle directory in the img
directory. My blog post pages have an article image and an image designated as featured
. All other web pages do not have an image designated as featured
.
In Front Matter for this web page, the entries look like this:
resources:
- name: featured
src: img/featured.png
title: Customized Open Graph integration
- name: article-img
src: img/open-graph.png
title: Customized Open Graph integration
Using .Resources.GetMatch
, I fetch the image designated as featured
from the PageBundle and store it in the $image
variable. If a featured
image is present, I output the appropriate meta tags for that image.
If no featured
image is present, I search for the tekki-tipps-og.png
image using resources.Get
and store it in the $ogimage
variable. I saved the image in the static
directory at the top level.
The images should have a resolution of 1200px X 600px to display a good image even on high resolution screens. See also the Facebook link above.
meta property=“og:image:secure_url”
Nowadays there are hardly any websites that do not have an SSL certificate. The meta tag is from another time. But it must be specified. Also here I pass the .Permalink
of the image.
meta property=“og:image:type”
I use image files of type png
and jpg
. png
are smaller in size if a large part of the image contains the same color components. The type is automatically determined by .MediaType
.
meta property=“og:image:width”
With .Width
the width of the image is determined.
meta property=“og:image:height”
With .Height
the height of the image is determined.
meta property=“og:image:alt”
For the blog posts, I take the alternate text of the image from the Resource Title. For all other web pages, with the general image, I save my alt text manually.
Checking the Open Graph parameters
In the head
of the HTML source code you can look at the corresponding meta tags. Since I created the Open Graph meta tags after the fact, it is very time-consuming to check this for each post and language.
There is a solution for this as well - browser extensions. For Safari I haven’t found an extension at all. For Edge there is a text solution, but the relevant part has to be scrolled. Then you can also look at the HTML source code.
For Firefox and Chrome there is a corresponding extension. The one for Firefox I find nicer and use it too:


Conclusion
I do not have a Facebook account and therefore cannot control the sharing of links within Facebook. Sharing my contact addresses is too valuable to me to be forced to share with Facebook. Others see this differently and for these people I have integrated the OG meta tags.
Link list for this article
With the German language setting, comments are not displayed in the English version of the website and vice versa.