Torsten Bühl
Software Engineer & Founder

Hi, I'm Torsten. I am the founder of Foodlane, Company Signal, and Exceptiontrap (acquired by Scout APM). I strive to build simple and beautiful software products that people love to use. Learn more about them here

Create Twitter Cards and LinkedIn Previews with Rails

Torsten Bühl Profile

You want a nice preview when your content is shared on Twitter, LinkedIn, and other sites? That’s a good idea, because, well, it just looks better. Let’s build a simple solution.

In General

Most sites are looking for special meta tags to render their previews. These meta tags are defined by the Open Graph protocol. Additionally, Twitter introduced its own set for their Twitter Card previews.

But Twitter uses Open Graph tags as a fallback. Hence we use them and just add Twitter’s tags for specific information like the username. These are the tags we need:

Used by all sites

  • og:title
  • og:description
  • og:image

Additionally used by Twitter

  • twitter:card (i.e. summary, summary_large_image, photo, …)
  • twitter:creator (Twitter @username of the author of an article)
  • twitter:site (Twitter @username of the site, product, …)

The Implementation

First, you probably don’t need these previews for each page. It only makes sense for pages that will get shared – e.g. your home page and articles.

We want a simple and flexible solution. So we define a placeholder with yield in our layout file, and let the view template populate it with content_for. Here is the layout file:

# app/views/application.html.haml
%html
  %head
    # ...
    = yield :og_header
    = tag(:meta, property: "twitter:site", content: "@exceptiontrap")

The site username is the same for all pages. That’s why we put it directly in the layout file.

In our views, we define the meta tags within a content_for block. The home page view could look like this:

# app/views/home/index.html.haml
- content_for :og_header
  = tag(:meta, property: "og:title", content: "The Title")
  = tag(:meta, property: "og:description", content: "The Description")
  = tag(:meta, property: "og:image", content: asset_url("summary-large-card-tweet.png"))
  = tag(:meta, property: "twitter:card", content: "summary_large_image")

We use the asset_url helper to specify the image, because we need the absolute URL, and want to use the asset pipeline. The image would be stored in app/assets/summary-large-card-tweet.png.

Here is an example of what the article page view could look like:

# app/views/blog/articles/show.html.haml
- content_for :og_header do
  = tag("meta", property: "og:title", content: @article.title)
  = tag("meta", property: "og:description", content: strip_tags(@article.excerpt))
  = tag("meta", property: "og:image", content: asset_url("logos/logo-square-250x250.png"))
  = tag("meta", property: "twitter:card", content: "summary")
  = tag("meta", property: "twitter:creator", content: "@tbuehl")

Do you find yourself inserting the same code in a lot of views, or do you want a default for all pages? A “default setting” in your layout could be one way to solve this. For example:

# app/views/layouts/application.html.haml
%html
  %head
    # ...
    if content_for?(:og_header)
      = yield :og_header
    - else
      = render "layouts/default_og_header"
# app/views/layouts/_default_og_header.html.haml
= tag(:meta, property: "og:title", content: "The Title")
= tag(:meta, property: "og:description", content: "The Description")
# ...

Note: The first time you use Twitter Cards, you need to activate them with the Card Validator. Which doesn’t make much sense to me, but that’s how it works (in 2015).

Let me know

Any feedback? Just ping me at @tbuehl