Testimonials — Card Grid

Testimonial grid with avatar, name, role, and quote cards.

Testimonials cardavatar

Installation

npx shadcn@latest add @saastro/testimonials-01

Preview

What people are saying

Hear from developers who use our blocks.

These blocks saved me hours of work. The quality is outstanding and they integrate seamlessly with my Astro site.

SC

Sarah Chen

Frontend Developer, Vercel

Finally a block library that gets the developer experience right. Install, customize, ship.

MR

Marco Rivera

CTO, StartupCo

The attention to detail in these components is impressive. Responsive, accessible, and beautifully designed.

EP

Emily Park

Designer, Studio

Source Code

import { Card, CardContent } from '@/components/ui/card';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { cn } from '@/lib/utils';

type Testimonial = {
  quote: string;
  author: string;
  role: string;
  company?: string;
  avatarUrl?: string;
};

type Testimonials01Props = {
  title?: string;
  description?: string;
  testimonials: Testimonial[];
  className?: string;
};

function getInitials(name: string): string {
  return name
    .split(' ')
    .map((n) => n[0])
    .join('')
    .toUpperCase()
    .slice(0, 2);
}

export function Testimonials01({
  title,
  description,
  testimonials,
  className,
}: Testimonials01Props) {
  return (
    <section className={cn('w-full px-6 py-24 md:py-32', className)}>
      <div className="mx-auto max-w-6xl">
        {(title || description) && (
          <div className="mx-auto mb-16 max-w-2xl text-center">
            {title && <h2 className="text-3xl font-bold tracking-tight sm:text-4xl">{title}</h2>}
            {description && <p className="mt-4 text-lg text-muted-foreground">{description}</p>}
          </div>
        )}
        <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
          {testimonials.map((testimonial, index) => (
            <Card key={index} className="flex flex-col justify-between">
              <CardContent className="pt-6">
                <p className="text-sm italic text-muted-foreground">
                  &ldquo;{testimonial.quote}&rdquo;
                </p>
                <div className="mt-6 flex items-center gap-3">
                  <Avatar className="h-10 w-10">
                    {testimonial.avatarUrl && <AvatarImage src={testimonial.avatarUrl} />}
                    <AvatarFallback className="text-xs">
                      {getInitials(testimonial.author)}
                    </AvatarFallback>
                  </Avatar>
                  <div>
                    <p className="text-sm font-semibold">{testimonial.author}</p>
                    <p className="text-xs text-muted-foreground">
                      {testimonial.role}
                      {testimonial.company && `, ${testimonial.company}`}
                    </p>
                  </div>
                </div>
              </CardContent>
            </Card>
          ))}
        </div>
      </div>
    </section>
  );
}

Registry Setup

Add the Saastro registry to your components.json:

{
  "registries": {
    "@saastro": "https://ui.saastro.io/r/{name}.json"
  }
}