Source Code

<div class="rfi-phone">
  <div class="rfi-screen">
    <div class="rfi-status"><span>9:41</span><span class="rfi-batt"><i></i></span></div>

    <header class="rfi-head">
      <button class="rfi-back" aria-label="Back">&#8249;</button>
      <b>Invite Friends</b>
      <span class="rfi-spacer"></span>
    </header>

    <div class="rfi-body">
      <div class="rfi-hero">
        <div class="rfi-hero-icon">&#127873;</div>
        <h1>Give $10, Get $10</h1>
        <p>Invite friends to Northline. They get $10 off their first order, and you get $10 credit when they place it.</p>
      </div>

      <div class="rfi-code-card">
        <span class="rfi-code-label">Your invite code</span>
        <div class="rfi-code-row">
          <span class="rfi-code" id="rfiCode">NORTH-7QK2</span>
          <button class="rfi-copy-btn" id="rfiCopyBtn">Copy</button>
        </div>
      </div>

      <div class="rfi-share-row">
        <button class="rfi-share" data-channel="message">
          <span class="rfi-share-icon" style="background:#dcfce7">&#128172;</span>
          <span>Message</span>
        </button>
        <button class="rfi-share" data-channel="email">
          <span class="rfi-share-icon" style="background:#dbeafe">&#128231;</span>
          <span>Email</span>
        </button>
        <button class="rfi-share" data-channel="link">
          <span class="rfi-share-icon" style="background:#fef3c7">&#128279;</span>
          <span>Copy Link</span>
        </button>
        <button class="rfi-share" data-channel="more">
          <span class="rfi-share-icon" style="background:#f1f5f9">&#8942;</span>
          <span>More</span>
        </button>
      </div>

      <div class="rfi-progress-card">
        <div class="rfi-progress-head">
          <b><span id="rfiInviteCount">2</span> of 5 invites redeemed</b>
          <span id="rfiEarned">$20 earned</span>
        </div>
        <div class="rfi-progress-track"><div class="rfi-progress-fill" id="rfiProgressFill"></div></div>
        <p class="rfi-progress-hint" id="rfiProgressHint">Invite 3 more friends to unlock the $50 bonus tier</p>
      </div>

      <div class="rfi-list">
        <p class="rfi-list-label">Your invites</p>
        <div id="rfiInviteList"></div>
      </div>
    </div>

    <div class="rfi-toast" id="rfiToast" hidden></div>
  </div>
</div>

Mobile Referral & Invite Friends Screen — Free Snippet

Mobile Referral & Invite Friends Screen · Mobile · Plain HTML, CSS & JS · Live preview

What's included

Features

Earned total and progress bar both computed from the same invites array via filter/reduce
Bonus-tier hint counts down exactly how many more invites are needed, no vague language
Four distinct share channels route through one handler with channel-specific toast messages
Invite list renders initials avatars, status text, and reward pills from live invite data
Simulated real-time redemption updates the progress, total, and list together with a toast
Copy button shows a genuine copied confirmation state, not just a static label
Responsive four-column share grid with hover lift feedback
Zero dependencies, vanilla JavaScript only

About this UI Snippet

Mobile Referral Screen — Copyable Code, Share Channels & Computed Progress

Screenshot of the Mobile Referral & Invite Friends Screen snippet rendered live

A referral program only works if sharing the code is nearly frictionless and the reward math is visibly trustworthy. This snippet builds a full invite screen around those two requirements: a one-tap copyable invite code with real share-channel buttons, and a progress bar plus earnings total computed directly from an actual list of invited friends rather than displayed as separate, hand-maintained numbers.

Earnings computed from the invite list, never hardcoded

renderProgress() filters the invites array for status === 'redeemed' and sums each one's reward with reduce() to produce the "$X earned" total. The redeemed count also drives the progress bar's width percentage against goal. Because every number on screen is derived from the same array, adding, removing, or changing the status of an invite anywhere in the array automatically keeps the count, the earned total, and the bar in agreement.

A bonus-tier hint that counts down honestly

hintEl computes remaining = bonusAt - redeemed.length and either tells the user exactly how many more invites unlock the next bonus tier, or switches to a celebratory "Bonus tier unlocked" message once remaining reaches zero. This is the kind of small honesty detail that makes a referral program feel real — the number shown is always mathematically exact, not a rounded or vague "almost there."

Four share channels, one shared feedback pattern

The message, email, copy-link, and "more" buttons all route through one click handler that looks up a channel-specific confirmation string in the channelLabels object and shows it as a toast. This keeps the visual feedback for every share action consistent while still telling the user specifically what just happened for the channel they picked.

A live invite list that updates itself

renderList() renders every invited friend as a row with an initials avatar, a status line, and either a "+$10" reward pill (redeemed) or a "Pending" pill (not yet redeemed). A setTimeout call later in the demo flips invites[2] from pending to redeemed and re-runs both renderProgress() and renderList() together — simulating a real-time push when a friend actually completes their first order, with a toast confirming exactly who just redeemed.

The invite code as a one-tap copy target, not just display text

Clicking "Copy" flips the button to a "Copied!" confirmation state, adds a completed-state class, and shows a toast naming the exact code that was copied — matching the low-friction expectation of a referral flow, where every extra tap between "I want to share this" and actually sharing it measurably reduces how many invites get sent.

Wiring it to a real referral backend

Replace the static invites array and the setTimeout-simulated redemption with a real API call that fetches the user's actual referral history and either polls or subscribes via WebSocket for redemption events, and wire navigator.clipboard.writeText() plus the real Web Share API (navigator.share()) into the copy and share-channel buttons in place of the toast-only demo feedback.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than tracing the derived-totals logic by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how renderProgress() computes the earned total and progress percentage from the invites array using filter and reduce, and how the bonus-tier hint text derives its exact remaining-invites count from the same data. The same assistant can help you optimize it, for instance asking whether the invite list and redemption events should come from a WebSocket subscription instead of the demo's single setTimeout-simulated redemption. It is also useful for extending the screen: ask it to wire in the real Clipboard API and Web Share API for the copy and share buttons, add a leaderboard comparing the user's referral count to friends, or support multiple simultaneous bonus tiers with different thresholds and rewards. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a mobile "referral and invite friends" screen in plain HTML, CSS, and JavaScript, framed inside a CSS phone mockup, no library.

Requirements:
- A hero section explaining the referral offer, a card showing a copyable invite code with a working "Copy" button that shows a temporary "Copied!" confirmation state, and a row of at least four distinct share-channel buttons (e.g. message, email, copy link, more) that each show a channel-specific toast confirmation message on click.
- A progress card showing how many invites have been redeemed out of a goal, a total dollar amount earned, and a progress bar — all three values must be computed live from a single JavaScript array of invite objects (name, status, reward) using array methods like filter and reduce, never hardcoded as separate numbers.
- A dynamic hint line below the progress bar stating exactly how many more invites are needed to unlock a bonus reward tier, computed from the same invite data, switching to a celebratory unlocked message once the threshold is reached.
- A list of individual invited friends below the progress card, each showing an avatar, a status line (joined vs. not yet joined), and a reward pill, rendered from the same invites array.
- Simulate a real-time redemption a few seconds after load: update one invite's status in the array, then re-render both the progress card and the invite list together, and show a toast announcing which friend just redeemed their invite.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSThe screen renders an invite code, share buttons, and a progress card showing 2 of 5 invites redeemed.
  2. 2
    Tap "Copy"The button confirms "Copied!" and a toast names the specific code that was copied.
  3. 3
    Tap a share channel buttonEach shows its own toast message describing that specific channel's action.
  4. 4
    Wait a few secondsA third invite automatically flips to redeemed, and the progress bar, earned total, and invite list update together with a toast announcing it.
  5. 5
    Edit the invites arrayAdd, remove, or change the status of entries in the JS panel to see the progress bar and totals recompute automatically.
  6. 6
    Wire it to a real backendReplace the static invites array with a fetched referral history and connect the copy/share buttons to the real Clipboard and Web Share APIs.

Real-world uses

Common Use Cases

Referral and affiliate reward programs
The canonical use case — a self-contained invite flow with code sharing, computed progress, and reward tracking in one screen.
Fintech and rewards-app onboarding
Pair the computed progress-bar pattern with account-opening bonuses that unlock at specific referral milestones.
Subscription and SaaS growth loops
Adapt the reward math for credit-based referral programs where each successful invite reduces a subscriber's bill.
Teaching derived-totals UI patterns
A concrete example of computing every displayed number (count, total, percentage) from one array with filter and reduce instead of separate hand-maintained values.
Related: Mobile Empty Cart Screen
See the Mobile Empty Cart Screen for a related conversion-focused mobile screen worth comparing against this one.
Related: Mobile Profile Edit Screen
See the Mobile Profile Edit Screen for a related account-management mobile screen worth pairing with this one.

Got questions?

Frequently Asked Questions

renderProgress() filters the invites array down to entries with status "redeemed", then sums each one's reward field with Array.prototype.reduce(). The total is never a separate hand-typed number, so it always matches the actual redeemed invites in the array.

It subtracts the current redeemed count from a bonusAt constant (remaining = bonusAt - redeemed.length) and renders that exact number in the hint text, switching to an "unlocked" message automatically once remaining reaches zero or below.

In this demo, it only shows a visual "Copied!" confirmation and a toast. Wire it to navigator.clipboard.writeText(codeEl.textContent) inside the click handler to perform a real clipboard copy.

A setTimeout call a few seconds after page load flips the third invite's status to "redeemed", sets its reward, and re-runs both renderProgress() and renderList() together, simulating a real-time push notification from a referral backend when a friend completes their first order.

All four route through one click handler that looks up a channel-specific message in the channelLabels object keyed by each button's data-channel attribute, so each channel shows contextually appropriate toast feedback from one shared code path.

Yes. Keep invites as state, derive the earned total and progress percentage in a computed/useMemo value using the same filter-and-reduce logic, and update the array (e.g. via a WebSocket event handler) to trigger a re-render of the progress card and list together.