Documentation menu

Countdown block

A live-ticking deadline timer for launches, sales and closing dates — rendered server-side as an animated GIF, the one technique that keeps actually counting down across every major email client, Outlook included.

How it works

No email client runs CSS animations or JavaScript reliably enough to animate a clock from scratch, so the countdown block doesn't try; it embeds a plain <img> pointing at a server-rendered animated GIF instead. On each fetch, MailInApp computes the time remaining until the configured deadline and encodes a fresh "DD:HH:MM:SS"-style digital-clock image from scratch (a from-scratch GIF89a encoder, not a wrapped library). It draws one second per frame for sixty frames, then freezes on the last frame, so the image ticks visibly for a full minute once opened, then holds. Because the image is regenerated from the real deadline at fetch time rather than pre-rendered once at send time, it's accurate no matter when or how many times a recipient opens the email. The endpoint also sends no-cache headers, so image proxies like Gmail's refetch on every open instead of reusing a stale cached frame.

The image URL itself is HMAC-signed (the deadline timestamp and color are baked into the signature), so it can't be used as a free-standing GIF-countdown renderer by anyone who isn't MailInApp. The endpoint is public, like the interactions and open-tracking endpoints, but only serves what it's been asked to sign.

There's no mail-vs-live-view divergence the way carousel or accordion have — both contexts embed the identical image endpoint. Mail uses an absolute URL (an email has no page origin to resolve a relative path against), while the live view uses an origin-relative one.

There's also no separate "fallback tier" beyond the image itself. A client that can't animate GIFs still shows the first frame — a meaningful, accurate snapshot of the time remaining, not a placeholder. The block's alt text and the email's plain-text part both carry the same remaining time in words (e.g. "Offer ends in 2d 4h"), so the deadline still reads even with images off entirely.

Once the deadline passes, the block stops rendering the GIF and switches to plain ended-state text instead — no more image request, no stale "00:00:00:00" hanging around.

Configurable fields:

  • Label — text shown above the ticking image, e.g. "Offer ends in".
  • Ends at — the deadline, picked as a date and time.
  • Color — the digit/text color, used both in the rendered GIF and the ended-state text.
  • Font size (px) — the label's font size while counting down.
  • Font size once ended (px) — the font size of the "This offer has ended" message shown after the deadline passes.

A countdown is pure content, like an image or text block — there's nothing to tap or submit, so it never generates an interaction event and never appears in the responses dashboard, the CSV export, or a project webhook.

Examples

Subject

Our biggest sale of the year starts now

A flash-sale email with a 24-hour deadline is the canonical use: the countdown visibly ticking down is what makes urgency legible at a glance. It keeps being accurate whether the recipient opens it the minute it lands or two days later, after the sale's already over, at which point it correctly shows the ended state instead of a frozen lie. A limited-inventory product drop can pair a countdown with a product block so both the clock and the stock count communicate urgency together. See Ecommerce & product drops for the pattern in full. An event-registration reminder can count down to the registration deadline (not the event itself; that's what add-to-calendar is for) to push fence-sitters to act before the window closes.

What the static fallback looks like

<div style="margin:12px 0">
  <p style="margin:0 0 6px;font-size:16px;font-weight:700;color:#111827">Offer ends in</p>
  <img
    src="https://mailinapp.com/api/countdown?t=1753401600000&c=111827&s=9f2a...&"
    width="207" height="34"
    alt="Offer ends in 2d 4h"
    style="display:block;max-width:100%;height:auto"
  >
</div>

There's no separate "fallback markup" beyond this: every client renders the same <img> tag. What differs is only whether that client animates GIFs (most do) or freezes on the first frame (a handful of legacy clients) — both cases still show an accurate remaining time.