DEV Community

Royal Simpson Pinto
Royal Simpson Pinto

Posted on

Shipping an embeddable widget behind one script tag on Cloudflare's free tier

Collecting testimonials is easy. Showing them is where everything falls apart.

You get a nice DM. Someone leaves a glowing comment. A customer emails you something kind. And then it just sits there. To actually put it on your site you screenshot it, crop it, drop the image into your page, and hope the alignment holds up on mobile. Six testimonials later you have six images of six different sizes, no way to moderate what shows, and no idea whether anyone who saw them clicked through. The next time you want to add one, you do the whole dance again.

I wanted the boring version of this to be one step: paste a script tag, and a wall of approved testimonials appears. That is ProofClip.

The core idea

ProofClip is a testimonial wall and social-proof generator for creators and small SaaS. There is a shareable form to collect testimonials, a dashboard to approve or hide them, a public wall of love, and an embeddable widget that drops into any site with a single tag:

<div data-proofclip="your-space-slug"></div>
<script src="https://your-proofclip.example/widget.js"></script>
Enter fullscreen mode Exit fullscreen mode

That is the whole integration. No React, no iframe, no build step on your end. The data-proofclip attribute names which space to render, and the script does the rest.

The whole thing runs on Cloudflare's free tier: Workers plus Hono for the API and the server-rendered pages, D1 (SQLite) for relational data, and R2 for uploaded images. There is no separate frontend build. The widget and card scripts are served as plain strings straight from the Worker.

How it works

Collection. Each workspace gets a public form at /c/<slug>. It takes text, a star rating, an optional photo, and a permission flag so the person is explicitly agreeing to be shown. You can also import proof you already have: POST /app/import lets you upload a screenshot of a DM, a comment, or a review, so testimonials that were never going to fill out a form still make it onto the wall.

Every testimonial lands in D1 with a status. The schema is blunt about it:

status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | hidden
Enter fullscreen mode Exit fullscreen mode

So nothing is public by default. New submissions sit in pending until you say otherwise.

Moderation. The dashboard at /app is where you approve, hide, or delete, all through POST /app/testimonial/:action. Because the public wall and the widget only ever read approved rows, moderation is the gate for everything downstream. There is a settings route too (/app/settings) for branding: name, accent color, logo, and a toggle for the "Collected with ProofClip" credit.

The wall and the widget. There is a hosted public wall at /w/<slug>, and there is the embed. The widget script finds every [data-proofclip] node on the page, fetches /api/wall/<slug> for that space, and renders a masonry-style column layout using CSS column-width, so cards flow into however many columns fit. All user text is escaped through a throwaway DOM node before it touches innerHTML, so a testimonial cannot inject markup into someone else's page. If branding is enabled for that plan, a small ProofClip credit is appended.

The widget also does light analytics. On render it fires a view event via navigator.sendBeacon, and it records a click event on interaction, both to POST /api/event. That is how a workspace sees widget views and clicks without any third-party tracker.

Cards. For turning a single testimonial into a share-ready image, /card.js runs a client-side canvas that exports a PNG in 9:16, 1:1, or 16:9. No paid image API is in the loop; the rendering happens in the browser. The card studio is gated to Pro and above.

Plans. Limits live in src/plans.ts and are enforced server-side, not just hidden in the UI. Free allows one space, ten testimonials, and one widget, with the branding credit on. Starter ($19) raises the testimonial and widget caps and lets you remove branding. Pro ($39) unlocks unlimited testimonials, multiple spaces, and the card generator. Agency ($79) goes wider still. The accounts.plan column is the single source of truth, and a protected webhook (POST /api/billing/activate, plus a Gumroad-shaped route) flips it after a purchase.

One honest limitation

The paid-tier roadmap is real work that is not done yet. Video testimonials, custom-domain wiring, team seats, and multiple widget configs per space are listed as not built. The data model was designed to support them, but "the schema has a column for it" is not the same as "it ships." There is also no provider-specific webhook signature verification yet. Billing activation is guarded by a shared secret header, which is fine for a hosted payment link plus a webhook, but it is not the same as verifying a signed payload from a specific provider. If you deployed this to take real money, that is the first thing I would harden.

I would rather say that plainly than imply the feature grid is fuller than it is. The collect, moderate, embed, and card loop is the part that actually works end to end, and that is the part most people needed anyway.

Why this shape

Most testimonial tools are either a heavyweight SaaS with a monthly floor that does not make sense for a solo creator, or a pile of manual screenshots. I wanted the middle: something a single person can self-host on a free tier, that treats moderation as a first-class step, and that embeds with the least possible ceremony. Serving the client scripts as strings from the Worker, keeping the data in D1, and putting images in R2 means there is no infrastructure to babysit and nothing that bills you while it sits idle.

If you want to walk the flow yourself, the README covers local dev: sign up, grab the API key, open the collection link, submit a testimonial, approve it, and view the wall and the embed.

Repo: https://github.com/royalpinto007/ProofClip

Top comments (0)