Social Proof Popup — Rotating Purchase Notification

Social Proof Popup · Modals · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Rotating event queue
Multiple distinct events cycle automatically, reading as ongoing activity rather than one static claim.
Zero-dependency generated avatars
Avatars are inline base64 SVGs generated per event color, with no image hosting or network request required.
Plausible relative timestamps
A fixed "X minutes ago" per event with a small live-style dot indicator, without the complexity of an actually ticking counter.
Permanent, respected dismissal
Closing the toast stops the rotation entirely for the session — it never reappears with the next queued event.
Auto-hide per toast
Each shown event automatically hides itself after about 5 seconds even without a dismiss click, keeping the page uncluttered.
Hover-revealed dismiss button
The ✕ control stays out of the way until hovered, avoiding visual clutter on a toast that's mostly meant to be glanced at.
Smooth slide-and-fade entrance
The toast animates in with opacity and transform only, staying smooth across every framework export.
Easily themeable accent colors
Each event's avatar color is just a hex value in its data object — no CSS changes needed to add new color variety.

About this UI Snippet

Social Proof Popup — Auto-Cycling Purchase Toasts with a Persistent Dismiss

Screenshot of the Social Proof Popup snippet rendered live

The small "Sarah from Austin just bought the Pro plan" bubble that drifts in near the corner of a landing page is a well-known conversion pattern — seeing recent, specific activity from real-seeming people builds more trust than a generic testimonial block. This snippet builds the complete rotating toast: a queue of events, automatic cycling on a timer, and a dismiss action that respects the user's choice for the rest of the session.

A queue of events, not a single static message

EVENTS is an array of { name, action, avatar, mins } objects, and cycle() advances through them in order (wrapping back to the start via rotateIndex % EVENTS.length), showing one at a time on a repeating interval. A single static "someone just bought this" message loses credibility the longer it sits on screen unchanged — rotating through several distinct, named events is what makes the pattern read as ongoing real activity rather than a fixed banner.

Generated avatar colors, not external images

Each event's avatar is a solid-color square generated on the fly as a tiny inline SVG, base64-encoded into a data: URI (avatarUri()) — no image hosting or placeholder service required, and no failed network request if the page is previewed offline. Swap this for a real photo URL per event once you have one; the <img> tag and its sizing don't need to change.

A relative timestamp that looks credible

Each event carries a mins value rendered as "X minutes ago" with a small pulsing-dot-style green indicator beside it — deliberately *not* a live-incrementing counter, since constantly ticking "1 minute ago" → "2 minutes ago" while a single toast is displayed would be more work for very little added believability; a fixed plausible time per event reads naturally without that complexity.

Dismiss means dismiss, for the whole session

Clicking the ✕ button doesn't just hide the current toast — it sets a dismissedManually flag that cycle() checks before showing anything, and clears the rotation interval outright. Once a user has explicitly said "stop showing me this," the pattern should respect that permanently rather than popping back up a few seconds later with the next event in the queue, which would undo the very trust the pattern is trying to build.

Where this pattern earns its keep, and where it backfires

Social proof works because specificity reads as truth — a named city and a named action feel real in a way "people love this!" never does. But the same specificity makes a fabricated or stale feed obvious the moment a visitor notices the same three names looping every visit, which is why real implementations should draw from an actually-updating event source rather than running a fixed demo list indefinitely in production.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to trace the rotation-and-dismissal state machine by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why avatarUri() base64-encodes a tiny inline SVG rather than pointing at a placeholder image service, or why dismissedManually is checked inside cycle() rather than simply calling clearInterval once and trusting that alone. The same assistant can help optimize it, for instance checking whether the two independent timers (hideTimer for auto-hide, cycleTimer for rotation) could ever race and show a toast for less than its intended duration if a click happens at just the wrong moment. It is just as useful for extending the pattern: ask it to persist the dismissal in localStorage so it survives a page reload within a cooldown window, replace the fixed mins values with real createdAt timestamps computed live, or fetch the EVENTS queue from a real backend endpoint instead of a hardcoded array. 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 rotating "social proof" purchase notification toast in plain HTML, CSS, and JavaScript — no libraries, no external image requests.

Requirements:
- A fixed-position toast in a page corner, hidden by default via opacity and transform (not display:none), that becomes visible by toggling a single CSS class so the transition animates smoothly.
- A JavaScript array of multiple distinct event objects (name, action text, an accent color, and a relative "minutes ago" number) — not a single static message — that the toast cycles through in order, wrapping back to the start once it reaches the end.
- Generate each event's avatar as a solid-color circle without any external image request or placeholder service: build a tiny inline SVG string colored per event, base64-encode it, and assign it as the image element's src via a data: URI.
- Each shown toast must automatically hide itself after a fixed duration (a few seconds) even if the user takes no action, using its own independent timer separate from the rotation timer that advances to the next event.
- A close button that, when clicked, immediately hides the current toast AND permanently stops the rotation from ever showing another event for the rest of the page session — clicking dismiss must not just hide the current one while leaving the next one queued to appear later.
- The rotation must not start immediately on page load; delay the first appearance by a couple of seconds, then continue on a longer repeating interval (e.g. every several seconds) for subsequent events.
- Confirm in a comment why respecting an explicit dismissal permanently, rather than resuming rotation after a pause, is the more trustworthy design choice for this kind of notification.

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 JSAfter a short delay, a toast slides in near the bottom-left corner showing a purchase event with a colored avatar.
  2. 2
    Watch it auto-cycleEvery 8 seconds, the toast hides and a new event from the EVENTS queue appears in its place.
  3. 3
    Hover the toastA small ✕ dismiss button fades in at the top-right corner of the toast.
  4. 4
    Click dismissThe toast hides immediately and the rotation stops permanently for the rest of the session — it won't reappear with the next event.
  5. 5
    Edit the eventsChange any entry's name, action, avatar color, or mins value in the EVENTS array to customize the rotation content.
  6. 6
    Connect real activity dataReplace the static EVENTS array with recent orders/signups fetched from your backend, keeping the same showEvent()/cycle() rotation logic.

Real-world uses

Common Use Cases

E-commerce conversion optimization
The classic "recent purchase" notification pattern used by FOMO, Proof, and similar conversion tools.
SaaS landing pages
Show recent signups or upgrades to build trust before a visitor commits to creating an account.
Course and info-product sales pages
Display recent enrollments alongside a countdown timer or trial countdown for urgency plus social proof.
Marketplace and crowdfunding pages
Show recent backer or buyer activity to reinforce that a campaign or listing is actively gaining traction.
Event and ticket sales pages
Display recent ticket purchases to create urgency around a limited-availability event.
Learning respectful notification UX
A practical reference for an auto-cycling toast that still honors an explicit user dismissal — compare with a snackbar undo for action-based toast feedback.

Got questions?

Frequently Asked Questions

Fetch recent events from your backend (e.g. the last N orders or signups) on page load, map them into the { name, action, avatar, mins } shape EVENTS uses, and assign the result to EVENTS before the cycle() rotation starts — for fresher data, refetch periodically and merge new events into the queue.

An explicit dismiss is the strongest possible signal that a user doesn't want to see this pattern — popping the next event back up moments later would directly undo the trust the dismiss action was supposed to build. Respecting it for the whole session is the more honest choice, even though it costs you the remaining impressions.

In the close button's click handler, write a flag (and optionally a timestamp) to localStorage; on page load, check that flag before starting the cycle() rotation at all, and skip scheduling it entirely if the user dismissed it within your chosen cooldown window (e.g. the last 24 hours).

Store an actual createdAt timestamp per event instead of a fixed mins number, and compute the relative label ("3 minutes ago") at the moment showEvent() runs using a small time-formatting helper, so the displayed time reflects how long ago the event actually happened rather than a value baked into the data.

In React, keep the current event and visibility in useState and run the rotation interval inside useEffect with cleanup on unmount; in Vue, use ref()/onUnmounted for the same interval cleanup; in Angular, use a component field with ngOnDestroy. The dismissedManually flag and cycle logic are plain JavaScript and need no framework-specific changes.