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
baseURLset, Hugo'sabsURLfunction won't generate correct absolute URLs. - Using a single image string instead of array: Hugo's built-in template expects
imagesto be an array, not a string. - Image not in /static: images referenced in front matter should be in the
/staticfolder (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.