Skip to content
Marketing block

Re-Engagement Email

A win-back email to a dormant account that opens with a fact about their own data, not a feature list, and asks for one tap instead of a click back into a product they stopped opening.

151 linesNo dependencies
  • email
  • winback
  • re-engagement
  • churn
  • html email

What's included

  • components/email-winback.tsx
  • No runtime dependencies

Works with

  • React
  • Next.js
  • Tailwind CSS
  • TypeScript

npx hoverlab add email-winback

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Start a page with this section — add more, order them, and leave with the page source.

Preview

Subject: Your 1,204 saved items are still thereWe have not changed anything without telling you — here's what's waiting.

Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.

Source

components/email-winback.tsx
/**
 * <EmailWinback> — a re-engagement email to a dormant account, as HTML that
 * survives Outlook.
 *
 * The layout problem specific to a win-back: the reader has already decided
 * this product is not worth their time, so the email cannot open by asking
 * for more of it. It opens with the ONE reason they might be wrong — a
 * fact about their own account, not a feature list — and asks for the
 * smallest possible action: a single question with two answers, not a
 * click straight back into a product they stopped opening. A "yes, still
 * interested" that costs one tap is the whole design, because a win-back
 * that leads with the upsell is indistinguishable from the promo that
 * follows it into the same folder.
 *
 * See `email-product-announcement.tsx` for why the markup is nested
 * `<table role="presentation">`s and inline styles rather than flexbox and
 * a `<style>` block, why the footer carries a postal address and an
 * unsubscribe link, and why the preview is a sandboxed iframe rather than a
 * `<table>` rendered as JSX on this page.
 *
 * THE UNSUBSCRIBE LINK IS NOT SMALLER OR GREYER THAN USUAL. Sending to a
 * dormant list is exactly the send most likely to be marked as spam rather
 * than unsubscribed from, which costs sender reputation for every other
 * email a mailbox provider decides whether to deliver. Making the opt-out
 * easy to find is a deliverability decision here, not only a legal one.
 */

const BRAND = 'Acme'
const SITE_URL = 'https://acme.com'
const ACCENT = '#6366f1'
const POSTAL_ADDRESS = '548 Market Street, PMB 12345, San Francisco, CA 94104'
const LAST_ACTIVE = '4 months ago'

const SUBJECT = 'Your 1,204 saved items are still there'
const PREHEADER = "We have not changed anything without telling you — here's what's waiting."

function buildEmailHtml(): string {
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>${SUBJECT}</title>
</head>
<body style="margin: 0; padding: 0; background-color: #f4f4f5;">
  <div style="display: none; max-height: 0; overflow: hidden; opacity: 0;">
    ${PREHEADER}
  </div>

  <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f4f4f5;">
    <tr>
      <td align="center" style="padding: 32px 16px;">

        <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="width: 600px; max-width: 100%; background-color: #ffffff; border-radius: 12px; overflow: hidden;">
          <tr>
            <td style="padding: 32px 32px 0;">
              <div style="font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 18px; font-weight: 700; color: #18181b;">
                ${BRAND}
              </div>
            </td>
          </tr>

          <tr>
            <td style="padding: 24px 32px 0;">
              <h1 style="margin: 0; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 22px; line-height: 1.35; font-weight: 700; color: #18181b;">
                Your 1,204 saved items are still there
              </h1>
              <p style="margin: 12px 0 0; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 15px; line-height: 1.6; color: #52525b;">
                You have not opened ${BRAND} since ${LAST_ACTIVE}, and we have not deleted anything in the meantime. Before your account moves to our free tier's storage limit, we wanted to ask a smaller question than "come back": is this still useful to you at all?
              </p>
            </td>
          </tr>

          <tr>
            <td style="padding: 24px 32px 0;">
              <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="border: 1px solid #e4e4e7; border-radius: 10px;">
                <tr>
                  <td style="padding: 16px 20px;">
                    <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
                      <tr>
                        <td width="50%" align="center" bgcolor="${ACCENT}" style="border-radius: 8px;">
                          <a href="${SITE_URL}/login?ref=winback-yes"
                             style="display: block; padding: 11px 16px; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 14px; font-weight: 600; color: #ffffff; text-decoration: none; border-radius: 8px;">
                            Still interested
                          </a>
                        </td>
                        <td width="12"></td>
                        <td width="50%" align="center" style="border: 1px solid #e4e4e7; border-radius: 8px;">
                          <a href="${SITE_URL}/feedback/churn?ref=winback-no"
                             style="display: block; padding: 11px 16px; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 14px; font-weight: 600; color: #52525b; text-decoration: none; border-radius: 8px;">
                            Not anymore
                          </a>
                        </td>
                      </tr>
                    </table>
                  </td>
                </tr>
              </table>
            </td>
          </tr>

          <tr>
            <td style="padding: 20px 32px 0;">
              <p style="margin: 0; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 13px; line-height: 1.6; color: #71717a;">
                Either answer is useful to us, and neither commits you to anything today. If it was something specific that did not work, "not anymore" takes you to a two-question form — we read every one.
              </p>
            </td>
          </tr>

          <tr>
            <td style="padding: 24px 32px 32px;">
              <div style="border-top: 1px solid #e4e4e7; padding-top: 16px; font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 12px; line-height: 1.6; color: #a1a1aa;">
                You are getting this because your ${BRAND} account has been inactive for a while.
                <a href="${SITE_URL}/preferences" style="color: #71717a; font-weight: 600;">Update your preferences</a>
                &nbsp;or&nbsp;
                <a href="${SITE_URL}/unsubscribe?list=winback" style="color: #71717a; font-weight: 600;">unsubscribe from these emails</a>.
                <br /><br />
                ${BRAND}, ${POSTAL_ADDRESS}
              </div>
            </td>
          </tr>
        </table>

      </td>
    </tr>
  </table>
</body>
</html>`
}

export function EmailWinback() {
  const html = buildEmailHtml()

  return (
    <div className="rounded-2xl border border-border/60 bg-card/60 p-4">
      <div className="mb-3 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 text-xs text-muted-foreground">
        <span>
          <span className="font-semibold text-foreground">Subject:</span> {SUBJECT}
        </span>
        <span className="text-muted-foreground/80">{PREHEADER}</span>
      </div>
      <iframe
        title={`${SUBJECT} — email preview`}
        srcDoc={html}
        sandbox=""
        className="h-[600px] w-full rounded-xl border border-border/60 bg-white"
      />
    </div>
  )
}

Before you paste

  • Styling is Tailwind utility classes on semantic tokens (bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.
  • Nothing to install. No component library, no icon package.
  • Every prop has a default, so it renders standalone before you wire it up.

Where it goes

Drop it at components/email-winback.tsx and import it where you need the section:

import { EmailWinback } from '@/components/email-winback'

Not using React?

The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.

email-winback.html
<!--
  Re-Engagement Email — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block has no interactive behaviour, so nothing is missing.
-->
<div class="rounded-2xl border border-border/60 bg-card/60 p-4">
  <div class="mb-3 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 text-xs text-muted-foreground">
    <span><span class="font-semibold text-foreground">Subject:</span> Your 1,204 saved items are still there</span>
    <span class="text-muted-foreground/80">We have not changed anything without telling you — here&#x27;s what&#x27;s waiting.</span>
  </div>
  <iframe title="Your 1,204 saved items are still there — email preview" srcDoc="&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&amp;quot;utf-8&amp;quot; /&amp;gt;
  &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
  &amp;lt;title&amp;gt;Your 1,204 saved items are still there&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body style=&amp;quot;margin: 0; padding: 0; background-color: #f4f4f5;&amp;quot;&amp;gt;
  &amp;lt;div style=&amp;quot;display: none; max-height: 0; overflow: hidden; opacity: 0;&amp;quot;&amp;gt;
    We have not changed anything without telling you — here&amp;#x27;s what&amp;#x27;s waiting.
  &amp;lt;/div&amp;gt;

  &amp;lt;table role=&amp;quot;presentation&amp;quot; width=&amp;quot;100%&amp;quot; cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;0&amp;quot; style=&amp;quot;background-color: #f4f4f5;&amp;quot;&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td align=&amp;quot;center&amp;quot; style=&amp;quot;padding: 32px 16px;&amp;quot;&amp;gt;

        &amp;lt;table role=&amp;quot;presentation&amp;quot; width=&amp;quot;600&amp;quot; cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;0&amp;quot; style=&amp;quot;width: 600px; max-width: 100%; background-color: #ffffff; border-radius: 12px; overflow: hidden;&amp;quot;&amp;gt;
          &amp;lt;tr&amp;gt;
            &amp;lt;td style=&amp;quot;padding: 32px 32px 0;&amp;quot;&amp;gt;
              &amp;lt;div style=&amp;quot;font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 18px; font-weight: 700; color: #18181b;&amp;quot;&amp;gt;
                Acme
              &amp;lt;/div&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;

          &amp;lt;tr&amp;gt;
            &amp;lt;td style=&amp;quot;padding: 24px 32px 0;&amp;quot;&amp;gt;
              &amp;lt;h1 style=&amp;quot;margin: 0; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 22px; line-height: 1.35; font-weight: 700; color: #18181b;&amp;quot;&amp;gt;
                Your 1,204 saved items are still there
              &amp;lt;/h1&amp;gt;
              &amp;lt;p style=&amp;quot;margin: 12px 0 0; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 15px; line-height: 1.6; color: #52525b;&amp;quot;&amp;gt;
                You have not opened Acme since 4 months ago, and we have not deleted anything in the meantime. Before your account moves to our free tier&amp;#x27;s storage limit, we wanted to ask a smaller question than &amp;quot;come back&amp;quot;: is this still useful to you at all?
              &amp;lt;/p&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;

          &amp;lt;tr&amp;gt;
            &amp;lt;td style=&amp;quot;padding: 24px 32px 0;&amp;quot;&amp;gt;
              &amp;lt;table role=&amp;quot;presentation&amp;quot; width=&amp;quot;100%&amp;quot; cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;0&amp;quot; style=&amp;quot;border: 1px solid #e4e4e7; border-radius: 10px;&amp;quot;&amp;gt;
                &amp;lt;tr&amp;gt;
                  &amp;lt;td style=&amp;quot;padding: 16px 20px;&amp;quot;&amp;gt;
                    &amp;lt;table role=&amp;quot;presentation&amp;quot; width=&amp;quot;100%&amp;quot; cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;0&amp;quot;&amp;gt;
                      &amp;lt;tr&amp;gt;
                        &amp;lt;td width=&amp;quot;50%&amp;quot; align=&amp;quot;center&amp;quot; bgcolor=&amp;quot;#6366f1&amp;quot; style=&amp;quot;border-radius: 8px;&amp;quot;&amp;gt;
                          &amp;lt;a href=&amp;quot;https://acme.com/login?ref=winback-yes&amp;quot;
                             style=&amp;quot;display: block; padding: 11px 16px; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 14px; font-weight: 600; color: #ffffff; text-decoration: none; border-radius: 8px;&amp;quot;&amp;gt;
                            Still interested
                          &amp;lt;/a&amp;gt;
                        &amp;lt;/td&amp;gt;
                        &amp;lt;td width=&amp;quot;12&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;
                        &amp;lt;td width=&amp;quot;50%&amp;quot; align=&amp;quot;center&amp;quot; style=&amp;quot;border: 1px solid #e4e4e7; border-radius: 8px;&amp;quot;&amp;gt;
                          &amp;lt;a href=&amp;quot;https://acme.com/feedback/churn?ref=winback-no&amp;quot;
                             style=&amp;quot;display: block; padding: 11px 16px; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 14px; font-weight: 600; color: #52525b; text-decoration: none; border-radius: 8px;&amp;quot;&amp;gt;
                            Not anymore
                          &amp;lt;/a&amp;gt;
                        &amp;lt;/td&amp;gt;
                      &amp;lt;/tr&amp;gt;
                    &amp;lt;/table&amp;gt;
                  &amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
              &amp;lt;/table&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;

          &amp;lt;tr&amp;gt;
            &amp;lt;td style=&amp;quot;padding: 20px 32px 0;&amp;quot;&amp;gt;
              &amp;lt;p style=&amp;quot;margin: 0; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 13px; line-height: 1.6; color: #71717a;&amp;quot;&amp;gt;
                Either answer is useful to us, and neither commits you to anything today. If it was something specific that did not work, &amp;quot;not anymore&amp;quot; takes you to a two-question form — we read every one.
              &amp;lt;/p&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;

          &amp;lt;tr&amp;gt;
            &amp;lt;td style=&amp;quot;padding: 24px 32px 32px;&amp;quot;&amp;gt;
              &amp;lt;div style=&amp;quot;border-top: 1px solid #e4e4e7; padding-top: 16px; font-family: -apple-system, &amp;#x27;Segoe UI&amp;#x27;, sans-serif; font-size: 12px; line-height: 1.6; color: #a1a1aa;&amp;quot;&amp;gt;
                You are getting this because your Acme account has been inactive for a while.
                &amp;lt;a href=&amp;quot;https://acme.com/preferences&amp;quot; style=&amp;quot;color: #71717a; font-weight: 600;&amp;quot;&amp;gt;Update your preferences&amp;lt;/a&amp;gt;
                &amp;amp;nbsp;or&amp;amp;nbsp;
                &amp;lt;a href=&amp;quot;https://acme.com/unsubscribe?list=winback&amp;quot; style=&amp;quot;color: #71717a; font-weight: 600;&amp;quot;&amp;gt;unsubscribe from these emails&amp;lt;/a&amp;gt;.
                &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
                Acme, 548 Market Street, PMB 12345, San Francisco, CA 94104
              &amp;lt;/div&amp;gt;
            &amp;lt;/td&amp;gt;
          &amp;lt;/tr&amp;gt;
        &amp;lt;/table&amp;gt;

      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;" sandbox="" class="h-[600px] w-full rounded-xl border border-border/60 bg-white"></iframe>
</div>
  • This is rendered HTML, not a translation of the React source. The Tailwind classes carry the design and work in any framework.
  • It is one frame: the component in its initial state, with no props applied beyond the defaults.
  • Requires Tailwind, and the design tokens the classes reference (bg-card, text-muted-foreground, and so on). The template ZIPs ship a globals.css that defines them.

What each framework gets across the whole catalog — effects convert properly; this rung is markup.

For AI

The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.

See the prompt

3 more blocks in Email Templates

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse Email Templates

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once

More Email Templates blocks

View category
Open the full page for this block

Feature Launch Email

A feature-announcement email as HTML that survives Outlook — nested tables, inline styles and a hidden preheader, previewed in a sandboxed iframe so the styles cannot leak onto the page around it.

Email Templates173 linesNo deps
Open the full page for this block

Newsletter Digest Email

A periodic roundup of several stories in one email, each reduced to a kicker, a headline and one line — the shape a reader can scan in order instead of a wall of paragraphs.

Email Templates162 linesNo deps
Open the full page for this block

Promotional Offer Email

A time-boxed discount email that leads with the number, states a deadline rather than a live countdown email cannot run, and letter-spaces the code so it survives being copied out of an inbox.

Email Templates155 linesNo deps