What are Twitter cards?

A Twitter card is a fancy way of displaying a URL within a tweet. Instead of the text of the URL itself, Twitter displays a “box” containing an image, the title and description of the page the URL refers to, and the domain. The idea is to make it more appealing to follow URLs.

Any time a Twitter user tweets a URL, Twitter follows the URL and looks for Twitter-specific page metadata (<meta> tags). If it finds this card metadata, it creates a card in the body of the tweet instead of simply displaying the URL verbatim.

It behooves web publishers, if they expect their readers to be sharing links to their site via Twitter, to make the (minor) effort of generating the metadata that Twitter needs.

I thought it was a cool idea, and wanted to see how hard it would be to add card support to nimblemachines.com and muforth.dev.

It was pretty straighforward, but I had intermittent problems with one kind of card. I’ll talk about that later.

How am I using them?

I generate my web sites using a Lua script that consumes some page metadata (title, tags, description, and a few others) and a bunch of wiki-like markup, and from this generates an HTML page, along the way generating <meta> and <title> tags for all of the metadata.

In order to add support for Twitter cards, all I had to do was generate a few extra <meta> tags for Twitter.

Twitter defines several card types; I’m only using two of them: summary and summary_large_image.

My Lua generator checks for the presence of a “description” in the page metadata. If it finds one, it generates a card. If there is also a “twitter:image” in the metadata, it creates a summary_large_image card using the image specified; otherwise, a summary card. The image used for the summary card is a “default” image: a larger version of the “favicon” image for the site.

Twitter says that the image should be unique to the page, but if you don’t include an image, the card they produce has a silly-looking icon on it (of a scroll or document). Lots of people, including me, use a default image if none is specified.

This will make a lot more sense once you see the <meta> tags that are generated. I’m going to use two examples from my muforth site.

summary card

The HTML generated for the page Threaded code contains the following Twitter-specific <meta> tags:

  <meta name="twitter:card" content="summary" />
  <meta name="twitter:description" content="Threaded code is an extraordinarily simple and elegant way to
     implement language interpreters. Compared to bytecoded interpreters it is quite fast, and compilers
     for it are essentially trivial." />
  <meta name="twitter:image" content="https://muforth.dev/-/9s08_hc595_640x640.jpg" />
  <meta name="twitter:site" content="@muforth" />
  <meta name="twitter:title" content="Threaded code" />

twitter:card is the card type; twitter:site is the Twitter handle associated with the domain. In this case, I have registered @muforth as a “project news feed” for muforth.

twitter:description and twitter:title contain the same information as the HTML <meta name="description"> and <title> tags.

Here is a tweet that embeds the above summary card:


summary_large_image card

The HTML generated for the page Getting started with RISC-V, contains the following Twitter-specific <meta> tags:

  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:description" content="Using muforth and the HiFive1 board to explore the world of RISC-V." />
  <meta name="twitter:image" content="https://muforth.dev/-/hifive1-board.jpg" />
  <meta name="twitter:site" content="@muforth" />
  <meta name="twitter:title" content="Getting started with RISC-V" />

The only difference from the first example is the card type. Here we are requesting a summary_large_image card instead of a summary card (because there was a twitter:image called out in the page metadata).

Here is a tweet that embeds the above summary_large_image card:


Issues with my journal (blog) pages

I publish my blog in an unusual – and sometimes problematic – way. Each year is a contiguous HTML page. Entries are simply HTML headings. And while it is possible, using fragment id’s, to link to a particular heading – thus I have a sort of “permalink” capability – the metadata that Twitter needs to create a card refers to the page as a whole – ie “2021 journal”. In order to fix this I would have to break each blog entry into its own page... Which is something that I have thought about doing.

But as it stands, it would be confusing for Twitter to generate cards for the journal pages, because the card would always say something like “2021 journal: my thoughts for the year”, and not say anything about the particular heading that is being shared (again, by using a fragment id).

Since my generator only generates the Twitter card metadata if there is a description field in the page metadata, I simply leave it out for the journal pages.

It works perfectly ... right?!?

When I first tried this out, it seemed to work very well. Twitter supplies a handy card validator that you can use to test out your card markup. (You have to be logged in to Twitter to use it.) By pasting in a URL that (presumably) contains Twitter metadata, you can see what Twitter would generate.

While I never saw any problems when tweeting URLs that generate summary_large_image cards, with summary cards I noticed that the card very often did not have an image associated with it. If I tried a failing URL in the validator, it worked. And if, after first unsuccessfully tweeting and then validating, I then refreshed the tweet, the image would show up!

I hate intermittent failures.

I re-read the documentation, and it said that Twitter “prefers” 1:1 aspect ratio images, between 150x150 and 4096x4096 pixels in size, for the summary card images. I was using non-square images when I saw the intermittent behavior. I have since created special square images for the sole purpose of having a default that might make Twitter happier. I haven’t seen any hiccups since then!

My advice is this: make sure to always use a square image – even if you have to go to some extra trouble to generate it – for summary cards.

The astute reader will realize something else: the tweet shown above that embeds a summary card also shows the URL. Usually the card replaces the URL, as in the summary_large_image example. Why is it showing both? Because the URL is followed by two hashtags?

There may still be some subtle bugs in the system!


I think Twitter cards are a neat idea, and I had fun making them work.

Facebook does a very similar thing – they call it “Open Graph” and the metadata key names start with “og:” – but because I hate Facebook I decided not to bother also generating meta tags for them!