Hugo Open Graph Tags: Built-in Templates and Custom Partials

Add og:title, og:image, og:description, and Twitter card tags to Hugo sites using the built-in opengraph template and custom partials with front matter data.

Hugo's built-in OG template

Hugo ships with a built-in opengraph internal template that generates OG and Twitter Card tags automatically. Add it to your baseof.html layout:

{{/* layouts/_default/baseof.html */}}
<!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}">
<head>
  <meta charset="utf-8">
  <title>{{ .Title }} | {{ .Site.Title }}</title>
  {{ template "_internal/opengraph.html" . }}
  {{ template "_internal/twitter_cards.html" . }}
</head>
<body>
  {{ block "main" . }}{{ end }}
</body>
</html>

Hugo's built-in template automatically reads .Title, .Description, .Params.images, and .Permalink from your content.

Front matter for OG data

Set page-level OG data in your content file's front matter:

---
title: "My Blog Post"
description: "A description for this specific post."
images:
  - "/images/posts/my-post-og.jpg"
date: 2026-03-15
tags: ["hugo", "seo"]
---

Post content.

The images array is used by Hugo's built-in OG template for og:image. If not set, Hugo falls back to the first image in the page content or a site-wide default.

Configure site-wide OG defaults

# config.toml (or hugo.toml)
[params]
  description = "My site description."
  images = ["/images/og-default.jpg"]
  twitterHandle = "@yourtwitterhandle"

# For hugo.yaml:
params:
  description: "My site description."
  images:
    - "/images/og-default.jpg"
  twitterHandle: "@yourtwitterhandle"

Custom OG partial for more control

If you need more control than the built-in template provides, override it with a custom partial at layouts/partials/og-tags.html:

{{/* layouts/partials/og-tags.html */}}
{{ $ogTitle := .Title | default .Site.Title }}
{{ $ogDesc := .Description | default .Site.Params.description }}
{{ $ogUrl := .Permalink }}
{{ $ogImage := "" }}
{{ if .Params.images }}
  {{ $ogImage = index .Params.images 0 | absURL }}
{{ else if .Site.Params.images }}
  {{ $ogImage = index .Site.Params.images 0 | absURL }}
{{ end }}

<meta property="og:title" content="{{ $ogTitle | htmlEscape }}" />
<meta property="og:description" content="{{ $ogDesc | htmlEscape }}" />
<meta property="og:url" content="{{ $ogUrl }}" />
{{ with $ogImage }}
<meta property="og:image" content="{{ . }}" />
{{ end }}
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta property="og:site_name" content="{{ .Site.Title | htmlEscape }}" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ $ogTitle | htmlEscape }}" />
<meta name="twitter:description" content="{{ $ogDesc | htmlEscape }}" />
{{ with $ogImage }}
<meta name="twitter:image" content="{{ . }}" />
{{ end }}
{{ with .Site.Params.twitterHandle }}
<meta name="twitter:site" content="{{ . }}" />
{{ end }}

Common Hugo OG mistakes

  • Missing baseURL in config: without baseURL set, Hugo's absURL function won't generate correct absolute URLs.
  • Using a single image string instead of array: Hugo's built-in template expects images to be an array, not a string.
  • Image not in /static: images referenced in front matter should be in the /static folder (served as-is) or in the page bundle.

Preview your OG tags free at OGFixer.com → Paste your Hugo site URL to verify how your link card looks on Twitter, LinkedIn, Discord, and Slack.

← All guides