Testimonial Grid

( Testimonials / Social Proof / Marketing )

A masonry grid of quote cards with avatars, roles and star ratings, anchored by one dark featured card.

Live preview and source code for Testimonial Grid

Testimonials

Loved by people who sweat the details

The animation details are the part I keep coming back to. It feels considered rather than decorated.
KFKai FischerFrontend Lead, Northbeam
Copy, paste, ship. No build step, no peer-dependency archaeology, no theme provider to wire up first.
ARAna RuizFounder, Studio Ninetysix
Accessible by default was the thing that sold my team. Focus states and labels were already handled.
OHOmar HaddadStaff Engineer, Cartogram
I used it for a client landing page on a Friday and nobody could tell it wasn't bespoke work.
LNLena NovakIndependent Designer

A social proof section built as a masonry grid of quote cards, each with a star rating, the quote, an avatar and a role, and one dark card that anchors the layout. The columns come from CSS column-count rather than a measuring script, so cards of different lengths pack without a layout library and the grid reflows on its own. Cards fade upward as they scroll into view, once each.

Installation

Copy the source from the Code tab above into your project as TestimonialGrid.tsx. Its styles are a <style> block of tg- prefixed classes that travels with the file, so there is no stylesheet to import.

It imports framer-motion for the scroll-in animation and lucide-react for the star:

Terminal
npm install framer-motion lucide-react

It also renders one item from this collection, so copy that file too and fix the import path at the top to match where you put it: Avatar.

Usage

Pass your own quotes; everything else has a default:

Example.tsx
import TestimonialGrid from "./components/TestimonialGrid";

const testimonials = [
    {
        quote: "We replaced three half-finished internal components with this in an afternoon.",
        name: "Priya Nair",
        role: "Design Engineer, Loomcraft",
        rating: 5,
        featured: true,
    },
];

export default function Example() {
    return <TestimonialGrid heading="Loved by people who sweat the details" testimonials={testimonials} />;
}

src on a testimonial sets the avatar image and falls back to coloured initials when it is missing. Omit rating and the stars disappear on that card. featured renders the card in the dark style.

When to reach for it

The proof section of a landing page, below the hero and above pricing, where five or six short quotes carry more weight than one long case study.

A launch page collecting early feedback, where the quotes vary in length. The column layout is more forgiving of that than an equal-height grid.

A page where one quote should dominate. Mark the strongest as featured and it becomes the dark card the eye lands on first.

When not to

If reading order matters. CSS columns fill top to bottom, so the second card sits under the first rather than beside it, and the visual order does not match the order in the array. That is fine for a wall of quotes and wrong for a ranked or narrative sequence.

If you have one testimonial. A single card in a three-column grid looks like a layout that failed rather than a deliberate pull quote.

If the quotes need to be interactive. There is no carousel, no expand, no link to a full story. Cards are static, and a quote long enough to need truncation will simply make its card taller than the rest.

Accessibility

Each card is a <figure> with a <blockquote> and a <figcaption>, which is the correct structure for an attributed quote, so the name and role are associated with the quote rather than floating near it.

The star row carries an aria-label giving the rating out of five, so the score does not depend on counting filled shapes. The heading is an <h2>, and the eyebrow above it is a plain <span>, so it does not add a level to the document outline.

The scroll-in animation is not gated on prefers-reduced-motion. Cards start offset and transparent, so removing the initial and whileInView values in the copied file is what honours that setting.

Notes

The default testimonials are fiction. Five invented names, roles and companies ship with the file so the block renders on its own. Replace all of them before the page goes anywhere near a user.

rating is compared per star, so a value like 4.5 fills four stars and nothing partial. Round before passing it.

Ratings are drawn as filled and unfilled stars in the same row, and on the featured card the unfilled ones are a translucent white rather than grey, which keeps a 4 out of 5 readable against the dark background.

The grid drops to two columns at 900px and one at 600px. With fewer cards than columns the layout still fills left to right, so three or more quotes is where it looks intentional.