Mansoor Barri

Dynamic OpenGraph Images on Hugo

Learn how to dynamically generate OpenGraph images for your Hugo site.

Dynamically generated OpenGraph images make your content look professional when shared on social platforms. While working on pdfthing, I discovered the next/og module, which allows you to create such images in Next.js.

Unfortunately, this module only works with Next.js, so I started exploring ways to implement the same functionality in Hugo. After some digging, I found a way to dynamically generate OpenGraph images using Hugo’s built-in tools.


Steps to Create Dynamic OpenGraph Images

1. Create a Partial

First, create a partial called opengraph.html. This partial will handle the generation of your OpenGraph image.

2. Define Variables

Set variables like title, description, and summary for your image content. Here’s an example:

{{ $title := .Title }}
{{ $description := .Description | default .Summary | truncate 150 }}

3. Generate a Unique Image Name

Create a unique name for each image to ensure proper linkage in the header.

```html
{{ $uniqueName := "" }}
{{ with .File }}
  {{ $uniqueName = printf "og/og-image-%s.svg" .UniqueID }}
{{ else }}
  {{ $uniqueName = "og/og-image-default.svg" }}
{{ end }}

4. Create the SVG Template

Define the basic structure of your SVG using Hugo’s templating syntax.

{{ $svg := printf `
<svg xmlns="http://www.w3.org/2000/svg" width="1900" height="630" viewBox="0 0 1900 630">
  <style>
    @font-face {
      font-family: 'Inter-SemiBold';
      src: url('/fonts/Inter-SemiBold.ttf') format('truetype');
    }
    @font-face {
      font-family: 'Inter-Light';
      src: url('/fonts/Inter-Light.ttf') format('truetype');
    }
    .title {
      font-family: 'Inter-SemiBold', Arial, sans-serif;
      font-size: 64px;
      fill: white;
      text-anchor: middle;
    }
    .description {
      font-family: 'Inter-Light', Arial, sans-serif;
      font-size: 32px;
      fill: white;
      text-anchor: middle;
    }
  </style>
  <rect width="1900" height="630" fill="black" />
  <text x="950" y="280" class="title">%s</text>
  <text x="950" y="400" class="description">%s</text>
</svg>
` $title $description | safeHTML }}

5. Process the SVG and Add Meta Tags

Generate the SVG using Hugo’s resources pipeline and include it in your meta tags.

{{ $generatedSVG := resources.FromString $uniqueName $svg | resources.ExecuteAsTemplate $uniqueName . }}

<!-- General Open Graph Meta Tags -->
<meta property="og:title" content="{{ $title }}" />
<meta property="og:description" content="{{ $description }}" />
<meta property="og:image" content="{{ $generatedSVG.Permalink }}" />
<meta property="og:image:type" content="image/svg+xml" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ .Permalink }}" />

<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ $title }}" />
<meta name="twitter:description" content="{{ $description }}" />
<meta name="twitter:image" content="{{ $generatedSVG.Permalink }}" />

<!-- Optional Additional Meta Tags -->
<meta name="author" content="{{ .Site.Title }}" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

The full code for this partial is available here: opengraph.html

6. Add the Partial to Your Header

{{- partial "opengraph.html" . -}}

Next Steps

This approach gives you a Hugo-compatible method to dynamically generate OpenGraph images. In the future, I plan to create a modular solution for Hugo that works similarly to next/og—making this process even easier.

Have questions or ideas? Let me know!