Read Time: 15 min

How to Code A Live Dynamic Twitter Feed in HTML Email


As we started nailing down logistics for Litmus Live (formerly the Email Design Conference) in 2015, conversations about making our launch email bigger and better than last year’s began. How do we top HTML5 video background technique in email? By using dynamic content: a live Twitter feed.

Yes, a Twitter feed in an email.

Our goals were twofold: generate interest in the conference and use an innovative—and inspiring—technique in the email to do so. After many brainstorming sessions, we decided to use the common approach of dynamic content, but with a twist.


View full email in browser →

You can also view the full code here and Litmus test results here to see how it displayed across 40+ email clients.

Dynamic Content in Email

Dynamic content in email is not a new concept. In fact, it’s frequently used to create personalized, targeted emails. Historically, dynamic content has been implemented strictly through the use of text or images, and pulled through merge tags or variables via an ESP. Dynamic images are linked to a single source file, which gets dynamically overwritten to display a new image for a certain subset of subscribers based on pre-defined personalization parameters. Both of these methods enable you to create a unique, personalized email experience for your subscribers.

We used dynamic images in our launch email to make the live dynamic Twitter feed work across several popular email clients. However, we also used an entirely brand new method to implement dynamic content: dynamic CSS.

While the dynamic CSS worked for WebKit clients, we needed to implement a proper fallback using dynamic imagery for our subscribers using non-WebKit clients. That being said, the live Twitter feed was supported (in one way or another!) in the following inboxes:

  • AOL Mail
    Dynamic Image
  • Apple Mail
    Dynamic CSS
  • iOS (Native email app)
    Dynamic CSS
  • Outlook (2000-2013)
    Dynamic Image
  • Outlook for Mac (2011 & 2016)
    Dynamic CSS
    Dynamic Image
  • Thunderbird
    Dynamic Image
  • Windows Phone
    Dynamic Image
  • Windows Mail
    Dynamic Image
  • Windows Live Mail
    Dynamic Image
  • Yahoo! Mail
    Dynamic Image

Dynamic CSS Content for WebKit Email Clients

While we could have used dynamic images for all email clients, we opted to use dynamic CSS for progressive enhancement in WebKit email clients, like the iPhone and iPad native email clients, to make the Twitter feed feel more realistic.

So how did we make it work? Using the fifteen most recent tweets that included the #TEDC15 hashtag, we displayed the first 5 tweets by default, then animated the remaining tweets one-by-one over a span of a minute. This allowed us to make the tweet stream feel real time and had the added benefit of keeping people engaged for longer.

While we updated the CSS file every 10 seconds, the actual content in the email couldn’t be updated in the same way—it needed a complete refresh of the document to work. Users needed to re-open the email or refresh the web version to see updated tweets. Although not ideal, it actually proved to be highly engaging!

To get the live twitter feed working, we used HTML and CSS that would only display in WebKit clients. To achieve this, we created empty <div>’s and <span>’s and used the content CSS property to add the content of Twitter users’ names, handles, timestamps, and tweet copy.

Here’s the HTML:

<div class="tweet" id="tweet-1">      
  <div class="tweet-avatar-wrapper">
    <div class="avatar"></div>
  <div class="tweet-wrapper">
    <span class="name"></span> <span class="handle"></span>
    <span class="timestamp"></span>
    <span class="copy"></span>

To dynamically overwrite the CSS, we relied on an external stylesheet that was updated every 10 seconds and included in our email like so:

<link rel="noopener" target="_blank" href="" type="text/css" rel="stylesheet" />

Here’s the corresponding CSS, with the tweet information in the content property:

#tweet-1 .name::before {
  content: "Kevin Mandeville";

#tweet-1 .handle::after {
  content: "@KevinMandeville";

#tweet-1 .copy::before {
  content: "I'm excited for #TEDC15! Who's going?";

#tweet-1 .timestamp::after {
  content: "1m";

The CSS for the tweets was wrapped in a media query that allowed us to display it only in WebKit-based email clients:

@media screen and (-webkit-min-device-pixel-ratio: 0) {
  /* Insert CSS here */

By using empty <div>’s for the default structure, the content did not show up for non-WebKit email clients. We then fell back to an image of the dynamic twitter feed for non-WebKit subscribers.

The only downside to using WebKit-targeting and the content property is that some email clients, such as Airmail and the Outlook iOS and Android app, would support the WebKit-targeted media query but not the content property, rendering the tweets invisible. But, given that these email clients are a very tiny part of our audience (less than 1%), this was a sacrifice worth making.

Dynamic Images for Non-WebKit Email Clients

For non-WebKit email clients, we used the more traditional method of displaying dynamic image since the CSS content property is not well supported in email clients outside of WebKit.

In the email, we referenced a dynamic image of the Twitter feed:

<img src="" width="600" height="902" border="0" class="webkit-hide" alt="tweet #TEDC15 to show up in the live feed!" style="color: #666666; font-size: 16px;"/>
tweet #TEDC15 to show up in the live feed!

We created a simple web page of just the Twitter feed using the same HTML and CSS from the email. We simply took a screenshot of the feed at the same dimensions of 600×902 through the command line utility wkhtmltoimage and dynamically updated that same image every 10 seconds.

Since we were using HTML and CSS for the WebKit view, we hid the image in WebKit to avoid duplicated feeds:

@media screen and (-webkit-min-device-pixel-ratio: 0) {
  .webkit-hide {
    display: none;

With this technique, the live Twitter feed worked in even the most troublesome clients (ahem, Outlook) and allowed the majority of our subscribers to join in the fun!

The only major downside to this dynamic image implementation is that Gmail caches its images. As a result, Gmail users didn’t experience the live Twitter feed. Instead, they saw eight of the most recent tweets and a special message to view the email’s web version for the full effect:



How We Built the Dynamic Twitter Integration

Kevin Thompson, our marketing developer, was the mastermind behind the actual Twitter integration. He created a very simple application built on the Sinatra framework and hosted on Heroku. You can check out the code and try it out for yourself by following the instructions on Github. This initial test proved it was possible to generate the tweets in CSS, load the external stylesheet in a handful of email clients, and that those clients could fetch the latest CSS each time the email was opened.

From there, the application began to get slightly more complex. Because Twitter imposes limits on the number of requests you make to the API, we needed to be sure that we wouldn’t exceed the limit of 150 requests per 15 minutes allotted for search queries. To address this issue, we added a second process to our Heroku application. This process ran in the background, fetching tweets every 10 seconds, and storing them in a cache. The main application process would then load tweets from the cache instead of searching for them directly through Twitter.

Kevin also had to take scalability and speed into consideration. While we had a solution for staying within Twitter’s API limits, our single server likely wouldn’t be able to process the expected number of requests from the 200,000+ recipients of our email. To solve this we implemented the Amazon CloudFront CDN, allowing us to support far more requests for our assets and also distribute them globally to ensure the files loaded quickly for our entire audience. In our Sinatra application, Kevin also added Cache-Control headers, which instructed CloudFront to expire assets after 10 seconds. This forced it to request new content from our application more frequently.

In order to keep the results as fresh as possible without exceeding the rate limits of Twitter’s API, we rendered and cached the dynamic CSS and image files every 10 seconds with the results of our Twitter search.

To manage the content of the tweets, both the Twitter search terms and blocked content/users were controlled using environment variables. Although changing environment variables on Heroku meant that our application would need to restart, because the assets were distributed through the CDN and the application was so simple, a restart only took a few seconds and would be completely unnoticeable. Also, because Heroku provides a user interface for editing environment variables, our marketing team was able to make changes to the search terms and block content as necessary.

If these methods seem too complicated or time-consuming for you, there are third party companies such as Movable Ink, LiveClicker, PowerInbox and Avari who specialize in dynamic content for email.

Filtering Bad Tweets

A big concern for us while incorporating a live dynamic twitter feed was relinquishing control of the content of our email—resulting in some “bad tweets” appearing in the stream. A few folks on Twitter pointed this out:

At the same time, we wanted to provide as close to a raw, unfiltered feed of tweets as possible to maintain the “wow” factor. Our hypothesis was that bad tweets would be an edge case scenario and would be cleared out of the feed through constant activity. So, we initially relied on Twitter’s search filters to weed out less than ideal content.

But, we wanted to have extra filters in place, so we also gave ourselves the ability to block specific usernames and text strings after fetching the results from Twitter. We ultimately decided against allowing tweet photos to be in the stream at all, as that could have become unruly and more harmful than just text. Also, it’s impossible to dynamically inject links via the content CSS property, so no links in any tweets worked either—they were simply displayed as text. All tweets were linked directly to the #TEDC15 tweet stream.

As a final failsafe, we had the option to disable realtime Twitter search results entirely, falling back to a filtered list of favorited tweets from our @emaildesignconf Twitter account. In the end, there were only a couple of bad tweets that needed to be removed. And, as of yet, we haven’t had to use the final failsafe of switching over to favorited tweets at all either—high five #emailgeeks!

The key? Making sure we tested everything from top to bottom. Ensure your designs look great in the inboxes your subscribers use with Litmus.

Test everything top to bottom

Preview emails on 50 + and breathe a sigh of relief when attempting even the craziest email feats, like a live Twitter feed.

Try Litmus free →


An overwhelmingly positive reaction

Our audience definitely loved this fun and unique implementation in email. It actually made the email an interactive and communal experience that everybody could participate in. The reactions from Twitter were priceless and even followed some similar themes.

Some people questioned if it was actually real:

Many just stared at it:

A bunch said hi to others:

There were several mentions of magic, witchcraft, and Harry Potter:

A slew of tomfoolery and shenanigans ensued, too:

To top it all off, a lot of people’s minds were blown:

A look at performance

We were blown away by the results from this campaign! Over 53% of our opens came in a WebKit email client, so many of our users saw the progressively enhanced version. In total, there were 750+ tweets about #TEDC15 in the first 24 hours after sending the email. Additionally, the email helped drive over 4,000 new visitors to our website and generated over 1,000 new prospects in that same amount of time! Not to mention, this email saw the best engagement we’ve seen from any email we’ve ever sent—almost 60% of users viewed the email for over 18 seconds!

Screen Shot 2015-05-26 at 3.48.42 PM

If you have any questions, please don’t hesitate to ask in the comments below, join the Litmus Community thread on the topic, or tweet @KevinMandeville and @KevinThompson!

Get Awesome Emails

Don’t miss our next mind-blowing email—sign up to receive news and information about everything happening at Litmus.

Kevin Mandeville

Kevin Mandeville

Kevin Mandeville was a Product Manager at Litmus