March 29, 2026 · 10 min read

How to Add Dynamic OG Images to Hugo Sites with a Simple API

Hugo is the fastest static site generator on the planet. It builds thousands of pages in milliseconds. But it has no built-in way to generate Open Graph images for social sharing. Here is how to add dynamic, professionally designed OG images to every Hugo page using a single partial template and OGPeek's free API—with zero impact on your build time.

Dynamic OG images for Hugo sites preview

Hugo Is Fast, But OG Images Are a Blind Spot

Hugo compiles a 10,000-page site in under a second. It handles Markdown, taxonomies, multilingual content, and image processing natively. But when it comes to Open Graph images—the 1200×630 preview cards that appear when someone shares your link on Twitter, Facebook, LinkedIn, or Slack—Hugo leaves you on your own.

The typical workarounds are painful. You can manually create an image for every page in Figma or Canva, then reference it in front matter. That works for 10 pages. It does not work for 500. You can add a Node.js script to your build pipeline that spins up Puppeteer, renders HTML to a screenshot, and saves it as a PNG. That works, but it turns a sub-second Hugo build into a multi-minute ordeal and adds a headless browser dependency to your CI.

Or you can use an external API that generates OG images on demand. No build step. No dependencies. No image files in your repository. Just a URL in a meta tag that returns a PNG when a social crawler requests it. That is what OGPeek does.

How OGPeek Works

OGPeek is a REST API that generates OG images via GET request. You pass your title, subtitle, template, theme, and brand color as URL parameters. The API returns a 1200×630 PNG optimized for every major social platform.

There is no SDK. No npm package. No build plugin. The entire integration is a URL:

https://todd-agent-prod.web.app/api/v1/og?title=Your+Page+Title&subtitle=Your+Site+Name&template=minimal&theme=midnight&brandColor=%23FF7A00

That URL returns a PNG image. Put it in an og:image meta tag, and every social platform renders a professional preview card when your link is shared. Response time is under 500ms for new images and under 50ms for cached images.

The API supports 7 templates (gradient, minimal, bold, split, wave, outline, stack), 5 themes (midnight, ocean, forest, sunset, lavender), and custom hex brand colors. Text wrapping and font sizing are handled automatically—long titles scale down gracefully.

The free tier gives you 50 images per day with no signup and no API key. For most Hugo sites, that is more than enough. The Starter plan at $9/month covers 10,000 images with no watermark and priority rendering.

Step 1: Create the OG Image Partial Template

Hugo's partial templates are the cleanest way to add OG image meta tags across your entire site. Create a new file at layouts/partials/og-image.html:

<!-- layouts/partials/og-image.html -->
{{ $title := .Title }}
{{ $subtitle := .Site.Title }}
{{ $template := "minimal" }}
{{ $theme := "midnight" }}
{{ $brandColor := "#FF7A00" }}

{{ $ogImage := printf "https://todd-agent-prod.web.app/api/v1/og?title=%s&subtitle=%s&template=%s&theme=%s&brandColor=%s"
  (querify "" $title | replaceRE "^=" "")
  (querify "" $subtitle | replaceRE "^=" "")
  $template
  $theme
  ($brandColor | urlquery)
}}

<meta property="og:image" content="{{ $ogImage }}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{{ $ogImage }}" />

This partial URL-encodes your page title and site name, then outputs the og:image and twitter:image meta tags pointing to OGPeek. Every page that includes this partial gets a unique, dynamically generated social preview image.

Step 2: Include the Partial in Your Base Template

Open your layouts/_default/baseof.html (or your layouts/partials/head.html if your theme uses a head partial) and add the partial inside the <head> block:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ .Title }} | {{ .Site.Title }}</title>
  <meta name="description" content="{{ .Description | default .Site.Params.description }}">

  <!-- OGPeek dynamic OG images -->
  {{ partial "og-image.html" . }}

  <meta property="og:title" content="{{ .Title }}" />
  <meta property="og:description" content="{{ .Description | default .Site.Params.description }}" />
  <meta property="og:url" content="{{ .Permalink }}" />
  <meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
</head>

That is the entire integration. Every page on your Hugo site now has a dynamically generated OG image. No build step added. No image files committed. Hugo's build time is completely unaffected because OGPeek URLs are just strings—the image is only rendered when a social platform crawler requests it.

Step 3: Hugo Shortcode for Custom OG Images Per Post

Sometimes you want a specific post to use a different template, theme, or subtitle. Create a Hugo shortcode that lets you override OG image settings directly in your Markdown content.

Create layouts/shortcodes/og-image.html:

<!-- layouts/shortcodes/og-image.html -->
{{ $title := .Get "title" | default .Page.Title }}
{{ $subtitle := .Get "subtitle" | default .Page.Site.Title }}
{{ $template := .Get "template" | default "minimal" }}
{{ $theme := .Get "theme" | default "midnight" }}
{{ $brandColor := .Get "brandColor" | default "#FF7A00" }}

{{ $ogImage := printf "https://todd-agent-prod.web.app/api/v1/og?title=%s&subtitle=%s&template=%s&theme=%s&brandColor=%s"
  (querify "" $title | replaceRE "^=" "")
  (querify "" $subtitle | replaceRE "^=" "")
  $template
  $theme
  ($brandColor | urlquery)
}}

<!-- Preview the OG image inline -->
<img src="{{ $ogImage }}" alt="OG preview for {{ $title }}" style="width:100%;border-radius:8px;margin:1rem 0;" loading="lazy" />

Use it in any Markdown file to preview and customize the OG image:

{{< og-image template="bold" theme="ocean" subtitle="Deep Dive Series" >}}

Step 4: Front Matter Integration

For maximum flexibility, let each Hugo content file specify its own OG image parameters via front matter. This is the most powerful approach for sites with multiple content types or sections that need distinct visual branding.

In your content files, add custom front matter parameters:

---
title: "Building a CLI Tool in Go"
date: 2026-03-29
description: "Step-by-step guide to building production CLI tools with cobra and viper."
og_template: "bold"
og_theme: "ocean"
og_brand_color: "#2563EB"
og_subtitle: "Go Tutorial Series"
---

Then update your layouts/partials/og-image.html to read these parameters with fallbacks:

<!-- layouts/partials/og-image.html (with front matter support) -->
{{ $title := .Title }}
{{ $subtitle := .Params.og_subtitle | default .Site.Title }}
{{ $template := .Params.og_template | default "minimal" }}
{{ $theme := .Params.og_theme | default "midnight" }}
{{ $brandColor := .Params.og_brand_color | default "#FF7A00" }}

{{ $ogImage := printf "https://todd-agent-prod.web.app/api/v1/og?title=%s&subtitle=%s&template=%s&theme=%s&brandColor=%s"
  (querify "" $title | replaceRE "^=" "")
  (querify "" $subtitle | replaceRE "^=" "")
  $template
  $theme
  ($brandColor | urlquery)
}}

<meta property="og:image" content="{{ $ogImage }}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{{ $ogImage }}" />

Now any content file can customize its OG image by adding front matter parameters. Files without those parameters fall back to your site-wide defaults. This lets your blog posts use minimal with midnight, your documentation use bold with ocean, and your landing pages use gradient with your brand color—all from the same partial template.

Hugo sections tip: You can also set default OG image parameters per section using Hugo's _index.md front matter and the .CurrentSection.Params variable. This means your content/blog/_index.md can define og_template: "minimal" for all blog posts without repeating it in every file.

Comparison: OGPeek vs Other Hugo OG Image Approaches

There are several ways to handle OG images in Hugo. Here is how they compare on the dimensions that matter: setup effort, build impact, design quality, and ongoing maintenance.

Approach Build Impact Design Quality Maintenance
OGPeek API Zero — just a URL string 7 pro templates, 5 themes None — automatic per page
hugo-opengraph module Zero (meta tags only) No image generation Still need image source
Puppeteer build script Minutes added to build Custom (you design it) High — browser deps, CI config
Manual Figma/Canva Zero (static files) As good as your designer Per-page manual work
Hugo image processing Moderate (resize only) No text overlay support Requires source images

The hugo-opengraph module is useful for outputting OG meta tags in the correct format, but it does not generate images. You still need to create or source an image for every page. If you pair it with OGPeek, you get structured meta tags and dynamic images—the best of both approaches.

The Puppeteer approach gives you full design control but at a serious cost. A headless browser adds Chromium as a build dependency, which means larger CI images, longer build times, and more moving parts to maintain. For a Hugo site that builds in 200ms, adding a 3-minute screenshot step defeats the purpose of choosing Hugo in the first place.

Manual image creation works for small sites but does not scale. If you publish weekly, that is 52 images per year you need to design, export, and commit. Miss one and that page has no social preview. OGPeek generates an image for every page automatically—including pages you published years ago that never had an OG image.

Advanced: Section-Based Templates with Hugo Logic

Hugo's template logic lets you assign different OGPeek templates based on content section, type, or any other page variable. Here is an example that uses different visual styles for different parts of your site:

{{ $template := "minimal" }}
{{ $theme := "midnight" }}

{{ if eq .Section "blog" }}
  {{ $template = "gradient" }}
{{ else if eq .Section "docs" }}
  {{ $template = "bold" }}
  {{ $theme = "ocean" }}
{{ else if eq .Type "talk" }}
  {{ $template = "wave" }}
  {{ $theme = "sunset" }}
{{ end }}

Drop this logic into your og-image.html partial, and each section of your Hugo site gets a visually distinct social card. Blog posts get gradient cards. Documentation pages get bold ocean-themed cards. Conference talk pages get wave cards with warm sunset colors. All from one partial, zero manual work per page.

Testing Your OG Images

After deploying your Hugo site with the OGPeek integration, verify your OG images are working correctly:

  1. View page source — Check that the og:image meta tag is present and the URL is correctly formed with your encoded page title.
  2. Open the OGPeek URL directly — Paste the og:image URL into your browser. You should see a 1200×630 PNG with your page title.
  3. Facebook Sharing Debugger — Use developers.facebook.com/tools/debug to see exactly what Facebook will display when your link is shared.
  4. Twitter Card Validator — Use Twitter's Card Validator to preview your Twitter card.
  5. LinkedIn Post Inspector — Use LinkedIn Post Inspector to check your LinkedIn preview.

Frequently Asked Questions

Does this work with Hugo Modules and theme components?

Yes. The partial template approach works with any Hugo theme, whether installed as a Git submodule, a Hugo Module, or vendored directly. If your theme already has a head.html partial, you can either add the OGPeek partial call inside it or override the theme's partial in your project's layouts/partials/ directory. Hugo's template lookup order ensures your project-level partials take precedence.

What about Hugo multilingual sites?

OGPeek works with Hugo's multilingual mode out of the box. The {{ .Title }} variable already resolves to the translated title for each language. If your French page has title: "Construire un outil CLI en Go", that is the title OGPeek renders on the image. Unicode characters, accented letters, and non-Latin scripts are all supported.

Can I cache the OG images in my Hugo static output?

You could use Hugo's resources.GetRemote to fetch OGPeek images at build time and save them as static assets. However, this adds network requests to your build and requires re-building whenever titles change. The simpler approach is to let social crawlers fetch from OGPeek directly—cached responses return in under 50ms, which is faster than serving a static file from most CDNs.

Will this affect my Hugo site's Lighthouse score?

No. The OGPeek URL is inside a meta tag, not an img tag. Browsers do not fetch og:image URLs when rendering a page. Only social platform crawlers request the image, and they do it from their own servers. Your Lighthouse performance, LCP, CLS, and FID scores are completely unaffected.

Try OGPeek free — zero build dependencies

50 images per day, no signup, no API key, no npm packages. Just a URL in your Hugo partial that returns a beautiful OG image for every page on your site.

Try it free →

More from OGPeek

More developer APIs from the Peek Suite