reviews /
Google Review Cards
Google-styled review cards with colored avatar initials, star ratings, verified badges, and an overall rating summary.
Preview
Source
tsx
"use client";
import { Star } from "lucide-react";
import { googleReviews } from "@/lib/placeholders";
const AVATAR_COLORS = [
"bg-blue-500",
"bg-emerald-500",
"bg-violet-500",
"bg-amber-500",
"bg-rose-500",
"bg-cyan-500",
];
function GoogleGLogo() {
return (
<svg viewBox="0 0 24 24" className="w-4 h-4" aria-label="Google">
<circle cx="12" cy="12" r="12" fill="white" />
<path
d="M21.805 12.227c0-.709-.063-1.39-.181-2.045H12v3.868h5.507a4.706 4.706 0 01-2.04 3.086v2.565h3.304c1.932-1.779 3.034-4.401 3.034-7.474z"
fill="#4285F4"
/>
<path
d="M12 22c2.763 0 5.079-.915 6.771-2.479l-3.304-2.565c-.916.614-2.088.977-3.467.977-2.665 0-4.924-1.8-5.732-4.22H2.862v2.646A10.002 10.002 0 0012 22z"
fill="#34A853"
/>
<path
d="M6.268 13.713A6.009 6.009 0 016 12c0-.595.082-1.174.268-1.713V7.641H2.862A10.002 10.002 0 002 12c0 1.614.386 3.141 1.062 4.499l3.206-2.786z"
fill="#FBBC05"
/>
<path
d="M12 5.567c1.501 0 2.848.516 3.908 1.528l2.932-2.932C17.075 2.475 14.759 1.5 12 1.5A10.002 10.002 0 002.862 7.641L6.068 10.287C6.876 7.867 9.135 5.567 12 5.567z"
fill="#EA4335"
/>
</svg>
);
}
function StarRating({ rating, size = "sm" }: { rating: number; size?: "sm" | "md" }) {
const cls = size === "md" ? "w-5 h-5" : "w-3.5 h-3.5";
return (
<div className="flex gap-0.5">
{Array.from({ length: 5 }).map((_, i) => (
<Star
key={i}
className={`${cls} ${i < rating ? "fill-amber-400 text-amber-400" : "fill-muted text-muted"}`}
/>
))}
</div>
);
}
export default function GoogleReviewCards() {
const avgRating = (googleReviews.reduce((s, r) => s + r.rating, 0) / googleReviews.length).toFixed(1);
return (
<section className="py-20 px-4 sm:px-6 bg-background">
<div className="max-w-4xl mx-auto">
{/* Overall rating summary */}
<div className="flex flex-col sm:flex-row items-center gap-4 mb-12 bg-card border border-border rounded-2xl p-6 shadow-sm">
<div className="flex flex-col items-center sm:items-start gap-1 sm:pr-8 sm:border-r sm:border-border">
<span className="text-5xl font-extrabold text-foreground">{avgRating}</span>
<StarRating rating={5} size="md" />
<span className="text-sm text-muted-foreground mt-1">Based on 127 reviews</span>
</div>
<div className="flex items-center gap-3 sm:pl-8">
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-white border border-border shadow-sm">
<GoogleGLogo />
</div>
<div>
<p className="font-semibold text-foreground">Google Reviews</p>
<p className="text-sm text-muted-foreground">Verified by Google</p>
</div>
</div>
</div>
{/* Review cards grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
{googleReviews.map((review, idx) => {
const initial = review.name.charAt(0).toUpperCase();
const colorClass = AVATAR_COLORS[idx % AVATAR_COLORS.length];
return (
<div
key={review.id}
className="bg-card border border-border rounded-2xl p-5 shadow-sm hover:shadow-md transition-shadow duration-300 flex flex-col gap-4"
>
{/* Reviewer row */}
<div className="flex items-center gap-3">
<div
className={`w-10 h-10 rounded-full ${colorClass} flex items-center justify-center text-white font-bold text-sm flex-shrink-0`}
>
{initial}
</div>
<div className="flex-1 min-w-0">
<p className="font-semibold text-sm text-foreground truncate">{review.name}</p>
<p className="text-xs text-muted-foreground">{review.date}</p>
</div>
</div>
{/* Stars */}
<StarRating rating={review.rating} />
{/* Review text */}
<p className="text-sm text-foreground leading-relaxed flex-1">{review.text}</p>
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-border">
<div className="flex items-center gap-1.5">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-white border border-border">
<GoogleGLogo />
</div>
<span className="text-xs font-medium text-muted-foreground">Google</span>
</div>
{review.verified && (
<span className="text-xs text-emerald-600 font-medium">Verified review</span>
)}
</div>
</div>
);
})}
</div>
</div>
</section>
);
} Claude Code Instructions
CLI Install
npx innovations add google-cardWhere to use it
Place near your contact, booking, or pricing section to build trust at conversion points.
In Astro:
import GoogleReviewCards from '../components/innovations/reviews/google-card';
<GoogleReviewCards client:load />
In Next.js:
import GoogleReviewCards from '@/components/innovations/reviews/google-card';
// Add near the contact form, pricing table, or CTA section
The overall rating summary shows "4.9 based on 127 reviews" — update the hardcoded count in the component to match your real review total. Edit individual reviews in src/lib/placeholders.ts.