Google Reviews widget
A reusable review feed built as a single custom-code embed you drop into any Webflow site. It fetches reviews, computes the average live, and lets visitors filter and sort, with no plugin and no monthly widget subscription. Built to demonstrate the exact task in the brief: writing custom code that integrates a data source into a pre-designed front end.
The live widget
This is the real component, running on this page. Tap a star filter or change the sort, the summary and list update instantly.
A working component, not a screenshot. The average, the star distribution bars, and the count all recompute from whatever is currently showing.
Built to actually work
Every interactive piece does something. This is the difference between a design mockup and a component a client can ship.
The code that powers it
The whole widget is one embed. Here is the core: fetch the data, compute the summary, render the list. This is written exactly as it would run against a live reviews endpoint.
In Webflow this goes into an Embed element (or the page's custom-code area). The fetch points at a data source that returns reviews as JSON. Swapping the mock URL for a real endpoint, a Google Places proxy or the client's own reviews table, is a one-line change.
// 1. Fetch reviews from the data source (JSON). // Swap REVIEWS_URL for a live endpoint or Places proxy. const REVIEWS_URL = "/data/reviews.json"; async function loadReviews() { const res = await fetch(REVIEWS_URL); if (!res.ok) throw new Error("Could not load reviews"); const data = await res.json(); return data.reviews; } // 2. Compute the summary from the data — never hard-coded. function summarize(reviews) { const total = reviews.length; const sum = reviews.reduce((a, r) => a + r.rating, 0); const average = total ? (sum / total) : 0; const dist = [5,4,3,2,1].map(s => reviews.filter(r => r.rating === s).length ); return { total, average, dist }; } // 3. Apply the active star filter + sort, then render. function apply(reviews, filter, sort) { let list = filter ? reviews.filter(r => r.rating === filter) : reviews; const by = { newest: (a, b) => b.time - a.time, oldest: (a, b) => a.time - b.time, highest: (a, b) => b.rating - a.rating, lowest: (a, b) => a.rating - b.rating, }; return [...list].sort(by[sort]); } // 4. Wire it up. const reviews = await loadReviews(); renderSummary(summarize(reviews)); renderList(apply(reviews, activeFilter, activeSort));