Twitter Cards with Nunjucks and 11ty

When sharing links on Twitter, there's a handy feature to make those links look prettier. Twitter "cards" will turn your simple text link into a rich card complete with an image and structured title and description data. Here's a recent tweet from my podcast's[1] account. When writing the tweet, we simply included the url https://aquestionofcode.com/52-what-gear-do-you-use/ in the text, and Twitter automatically added the card content.

There are several types of card, including summary, player, and summary with large image. They all serve different purposes and are activated in different ways. You can find the full details on Twitter's developer overview.

Adding support for Twitter Cards to your own site

Does this happen to all links? Sadly not. To activate this feature, the page you're linking to needs to include some Twitter-specific metadata in it's <head>. Here's what that metadata for this page looks like:

<meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content=https://twitter.com/thomashazledine />
<meta property="og:url" content="https://tomhazledine.com/twitter-cards-with-nunjucks-and-eleventy/" />
<meta property="og:title" content="Twitter Cards with Nunjucks and 11ty" />
<meta property="og:description" content="Using &quot;cards&quot; makes sharing your content on Twitter look much nicer." />
<meta name="twitter:image" content="https://tomhazledine.com/images/pages_stack_bg.jpg" />
<meta property="og:image" content="https://tomhazledine.com/images/pages_stack_bg.jpg" />

Adding the metadata in 11ty

My site is built with Eleventy (a.k.a. 11ty), and I use the Nunjucks templating engine. I chose Nunjucks because it's the de-facto standard in all the code example in the 11ty docs, and it's about as lightweight and intuitive as any other templating language.

That same block of metadata that I showed before looks like this in a Nunjucks file:

<meta name="twitter:card" content="{{cardType}}" />
<meta name="twitter:creator" content={{site.authorTwitterUrl}} />
<meta property="og:url" content="{{site.url}}{{page.url}}" />
<meta property="og:title" content="{{cardTitle}}" />
<meta property="og:description" content="{% if excerpt %}{{excerpt}}{% else %}{{site.summary}}{% endif %}" />
<meta name="twitter:image" content="{{cardImage}}" />
<meta property="og:image" content="{{cardImage}}" />

Because the way 11ty templates and layouts are put together, I had to add the metadata to my site's master main.njk layout file. This is the main layout that wraps all my pages, so I need to include some custom logic to account for a few scenaris:

  • Pages that don't have an image of their own (most pages on my site, in fact) fall back to using a generic site-logo image.
  • If we're using the generic site-logo image, I switch the card's type from summary_with_large_image to simply summary, which creates a smaller card that is better suited to a icon or simple logo.

Gotchas

I have to confess that it took me several attempts to get the images loading correctly[2]. Twitter provides a helpful Card validator. Having access to that meant I could test and debug without having to spam my followers with tweets full of broken card links (which was a relief, I can say). It still needs a valid real-world url to work, however, and that's not something my current dev-setup can provide for this site (where I normally just rely on a localhost url for testing and development). I just YOLO'd it by repeatedly deploying to my Netlify instance. Probably not "best practice", but it got the job done.


  1. #shamelessPlug
    A podcast, you say? Why yes! be sure to check out A Question of Code in your podcatcher of choice. ↩ī¸Ž

  2. Note to self, Twitter cards don't like relative paths for images! ↩ī¸Ž



Podcasts for Nerds

I often bore my friends by going on and on about great podcasts I've heard lately. But doing this one-on-one was getting a little stale, so I've launched a weekly podcast-recommendations newsletter so I can bug lots of people all at once!

    Newer post:

    CSS Naked Day

    Older post:

    The things I use