Jeremy Likness
Jeremy Likness
Empowering developers to be their best.
📅 Jul 12, 2019 🕘 4 min read 💬 663 words

Create an Article Preview in Hugo

Generate thumbnails on the fly and pull page metadata for a proper preview.

Part of the series: From Medium to Hugo

You are viewing a limited version of this blog. To enable experiences like comments, opt-in to our privacy and cookie policy.

One of my favorite Medium features is the ability to link any site and embed a preview with a thumbnail, title, and summary. For example, pasting a link to my Durable Dungeon GitHub repo:


Generates a preview like this:

Durable Dungeon Preview

Durable Dungeon Preview

I’ve since migrated to Hugo, so now what?

Hugo is extremely customizable and it is most likely possible to build the code to fetch a website and crawl metadata, but I decided to start with something a little more approachable and create previews for links internal to the blog. I know I can easily access the data through Hugo. The idea began when I implemented Google Search the first time. I noticed that some search results had thumbnails, but others didn’t. Upon researching this I discovered you can provide a thumbnail as metadata for a web page:

<meta name="thumbnail" content="thumbnail.jpg">

I always tag the main image for blog posts in my front matter using the image tag:

image: "/blog/myarticle/images/mainimage.jpg" 
 - "/blog/myarticle/images/mainimage.jpg"
 - "/blog/myarticle/images/anotherimage.jpg" 

Instead of pointing to a full-size image, I wanted to create a true thumbnail. I spent some time reading the docs for Hugo image processing and realized it wouldn’t be difficult at all. Hugo will process images inline and provide a link for the generated image. They are also placed in a resources folder, so they can be checked into source control and do not have to be regenerated each time.

⭐ Tip: Make sure you include your top-level resources folder in source control! I mistakenly included it my .gitignore and this was forcing Hugo to regenerate the images every time. Checking them in ensures they are only generated once and reused on subsequent passes. This makes the builds go faster and uses less storage.

The logic for my thumbnail generation looks like this, unrolled for visibility (in the template I have this in one line to save space in the generated content, but I also use the --minify option so that may be redundant). I removed redundant braces to make the logic easier to read but each statement is wrapped in double braces.

if .Params.image
    $original := print "images/" (path.Base .Params.image)
    $originalImg := .Resources.GetMatch $original
    if $originalImg 
        $thumbnailImg := $originalImg.Resize "90x"
        printf `<meta name="thumbnail" content="%s">`
            $thumbnailImg.RelPermalink | safeHTML

I first check that I’m on a page with image metadata. If so, I construct a path to the images based on how they are stored in the resources collection for the page. I parse the page resources for a match. If I get a match, I resize to 90 pixels wide, extract the link and print the meta tag.

After building this functionality I realized I could create a short code to do a similar thing with relative page links. I created a short code named relativelink.html:

{{$page := .Site.GetPage (.Get 0)}}
<div class="container alert alert-secondary">
    <div><a href="{{$page.RelPermalink}}" alt="{{$page.Title}}">
        <strong>{{ $page.Title }}</strong>
    <div class="float-left m-2">
    {{ if $page.Params.image }}
        {{ $original := print "images/" (path.Base $page.Params.image) }}
        {{ $originalImg := $page.Resources.GetMatch $original }}
        {{ if $originalImg }}
            {{ $thumbnailImg := $originalImg.Fit "200x100" }}
            {{ printf `<img src="%s" alt="%s">` 
                $thumbnailImg.RelPermalink $page.Title | safeHTML }}
    <div class="clearfix"></div>

The .Site.GetPage allows me to fetch a page based on its URL. I then extract the title, image, and description to create my preview. The thumbnail generation produces a slightly wider image and constrains the height as well. The result is that this short code:

{{<relativelink "/blog/migrate-from-medium-to-hugo" >}}

Generates this preview:

Migrate from Medium to Hugo

In June 2019 I migrated my blog from a Medium hosted solution to a self-hosted static website using Hugo. This post describes the steps of my journey including creating custom embeds, adding a Content Security Policy, and how I configured my Azure Storage Static Website and set up continuous deployment from GitHub to publish my blog.

This is a lot more interesting than a plain hyperlink and makes it easy for me to interlink blog posts.

What are your thoughts?


Jeremy Likness

Do you have an idea or suggestion for a blog post? Submit it here!
comments powered by Disqus

Part of the series: From Medium to Hugo

  1. Migrate from Medium to Hugo
  2. More Hugo Migration Tips
  3. Dynamic Search in a Static Hugo Website
  4. Create a Content Security Policy (CSP) in Hugo
  5. Create an Article Preview in Hugo
  6. Implement a Progressive Web App (PWA) in your Static Website