Is there a better way to implement social media cards?

Hi, I’ve recently finally implemented social media cards for my Hugo-based homepage and blog. Given my research it doesn’t seem to be a solved problem in general, so I’m planning on blogging about it.

But before I do that I wanted to make sure I don’t embarrass myself by missing something simple/obvious and would like to run it past y’all.

So what I do is using fmcardgen to create the cards to static/cards for the pages I want them (blog, talks, TILs) that look like this:

Then I check whether that card exists and if so use it in my baseof.html, otherwise I just use my avatar pic:

    {{- $card := (printf "cards/%s.png" .File.ContentBaseName) }}
    {{- if (os.FileExists (path.Join "static" $card)) -}}

    <meta property="og:image" content="{{ $card | absURL }}?{{ now.Unix }}">   {{/* cache bust cards */}}
    <meta property="og:image:width" content="909"/>
    <meta property="og:image:height" content="476"/>
    <meta property="og:image:type" content="image/png"/>

    <meta name="twitter:image" content="{{ $card | absURL }}?{{ now.Unix }}">
    <meta name="twitter:card" content="summary_large_image" />
    {{- else -}}
    <meta property="og:image" content="{{ "img/avatar_w200h200.jpg" | absURL }}">
    <meta property="og:image:width" content="200"/>
    <meta property="og:image:height" content="200"/>
    <meta property="og:image:type" content="image/jpg"/>

    <meta name="twitter:card" content="summary" />
    {{- end }}

My questions are:

  • is there a more straightforward way for card generation?
  • the logic around the paths looks convoluted – could it be simplified?

Thanks in advance – this forum has already helped me many times via Google. :slight_smile:

first have you take a look at the included partial included in Hugo to handle opengraph meta tags:

And the implementation used on the official website:

Then with Image Processing | Hugo you can generate these social cover images directly without saving them to a folder, you can resize these images to be at the right dimensions for social medias.


Correct me if I’m wrong, but it looks to me like what you’re suggesting is using the first picture from a post as the social card?

I just realized that I really should’ve been clearer…what I do it the following:

So just the topic, my name, and the base URL. I rarely have pictures part of my posts.

(I’ve edited the original question to make that clear and prevent further confusion)

How are you running fmcardgen? Is itmanually before you run Hugo? so it generates a card and saves it into your static folder, subfolder card. So when you run hugo, the file shall exist? or you integrate this somehow?

Why is this included in?

?{{ now.Unix }}

I don’t think Hugo can generate this cover image from scratch but the metadata tags can be omited if:

  1. Your posts are page bundles
  2. You place every cover generated in the correct post folder (at the root of this folder), and, in the name of that image you need at the end _cover.
  3. You use the internal opengraph template I linked earlier.

Last point, if you want a fallback image like in your code (avatar pic), you will need to declare it in your config.toml or config.yaml file in the images section

Yes exactly.

I have not found a good way to integrate this and I’m open to suggestions.

Whenever I change the design, I run

$ fmcardgen --recursive --config fmcardgen.toml -e md content/articles/ content/til/ content/talks/

But since I process them with ImageOptim which makes a big difference, but takes a long time, I now run it only when I need it as

$ fmcardgen --config fmcardgen.toml -e md PATH_TO_NEW

Cache busting. It’s otherwise borderline impossible to convince Twitter to refresh a card. I think they even suggest using ?{number} as a workaround.

OK, thanks for the input, I might use that for other problems.

For now I think I’m cool with my approach then, because I’m not a fan of all posts being called, since it still confuses most Markdown editors (as in “quick open gets flaky” :-/) and I don’t see an immediate benefit.

Is there a better way to check for the existence of the card though? It feels weird to use a combination of printf and Join.

Is there a better way to check for the existence of the card though?
It feels weird to use a combination of printf and Join.

Don’t think there is a better way, using printf is one way to create / concatenate strings, just for info you can do it these ways:

{{- $card := (printf "cards/%s.png" .File.ContentBaseName) }}
{{- $card := (print "cards/" .File.ContentBaseName ".png") }}
{{- $card := (delimit (slice "cards/" .File.ContentBaseName ".png") "") }}
{{- $card := (path.Join "cards" (print .File.ContentBaseName ".png")) }}
1 Like

@bwintx you should share your method using the image.Text approach here.

Truthfully, the Twitter conversation I had with you yesterday — and all the cool possibilities you mentioned — have me thinking about how to do that better, so that’s why I didn’t respond to this thread. :smiley:

(Besides, what I’m doing now is very similar to what’s already in the Hugo docs.)

Would y’all mind sharing the Twitter thread just to broaden my horizon?

You’ll have to click on the show replies link a few times. :slight_smile:

Thanks, I’ll do my best. :slight_smile:

1 Like