Back to blog
Engineering2026-04-186 min read

How Our Testimonial Widget Stays Under 5KB While Competitors Ship 65–125KB

Vanilla JS, closed Shadow DOM, async loading, and sendBeacon analytics. The concrete engineering decisions that kept Trustfolio's embed 10–25x smaller than Testimonial.to and Senja.

The benchmark (measured with Chrome DevTools, 2026)

People don't pick a testimonial tool because its widget is 4.8KB. But they stop evaluating alternatives the moment they realize the competitor's widget drops their Lighthouse score by 15 points. Here are the transfer sizes I measured on equivalent layouts:

WidgetLayoutTransfer (gzip)
Testimonial.toWall125 KB
SenjaCarousel65 KB
FamewallWall88 KB
TrustfolioWall4.8 KB
TrustfolioCarousel3.9 KB
TrustfolioBadge2.1 KB

Numbers came from Chrome DevTools' Network tab with "Disable cache" and "Slow 3G" enabled. Vendor marketing numbers ignored.

Choice 1 — Ship vanilla JS. No framework runtime.

Every competitor I audited ships React, Vue, or a micro-framework at runtime. React plus ReactDOM alone is ~45KB gzip — already 9x our entire widget. Our embed is a single IIFE: read the data-trustfolio attribute, fetch JSON from an edge route, render DOM directly. No JSX, no virtual DOM, no reconciler. Layout functions assemble DOM nodes and each handler is lazy-loaded on first interaction.

Choice 2 — Shadow DOM, not CSS-in-JS.

Testimonial widgets live inside hostile host styles. Your widget might render inside a WordPress theme with * { box-sizing: border-box }, or a Tailwind Preflight that strips every default margin. Most vendors ship styled-components or emotion to fight this. We use a closed Shadow DOM: the shadow root has its own scope, no host style leaks in, no widget style leaks out. The browser does the work. Our stylesheet is a single declarative <style> element — zero runtime cost.

Choice 3 — async script + navigator.sendBeacon

The embed script is async, so it never blocks render. Impression and click tracking use navigator.sendBeacon: fire-and-forget, zero perf penalty on user interaction. No third-party tracking scripts, no cookies, no GDPR complexity. The widget collects exactly what a reasonable user would expect — nothing more.

Choice 4 — No web fonts.

We render testimonials in font-family: system-ui, -apple-system, sans-serif. Native UI font on every platform. Zero KB download. Zero FOIT/FOUT. Competitors loading Google Fonts pay 20–80KB per weight for a slightly prettier wall of quotes. Not worth it.

Choice 5 — Bundle once, not per layout.

All six layouts (wall, carousel, badge, popup, marquee, card) share one bundle. Dead-code elimination via esbuild removes unused layouts at build; the runtime itself is one file. Carousel users download ~0.7KB of wall-layout code they'll never run. The tradeoff: one HTTP request, one cache entry, no version-skew debugging across layout permutations.

What this unlocks for customers

  • Shopify product pages that stay above 95 on Lighthouse with social proof embedded.
  • Framer landing pages that keep their signature "single HTTP request" speed.
  • WordPress sites that don't need a plugin — just a <script> tag.

The wider point

Modern SaaS ships weight by default because frameworks make it easy and bundlers don't warn you until it's too late. Testimonial widgets are a small niche, but the pattern repeats across every embedded SaaS asset — chat bubbles, cookie banners, analytics scripts, A/B test agents. They accumulate until your site is 60% third-party JS.

If you're building one, the question worth asking is: what's the smallest thing that still delivers the feature? Not "what's the easiest stack to reach for?"

Tools that pair well with Trustfolio

Sponsored — we earn a commission if you sign up through these links.

Sponsored

Ready to collect testimonials?

Start collecting and displaying customer testimonials in minutes. Free to get started.

Try Trustfolio Free