Skip to content
All Taste components

Testimonials

Testimonials — compact quote list

Eight one-line quotes with small avatars in a tidy two-column list, for lots of voices in little space.

8 short quotes · avatar + name + role · ruled rows · 2 columns, 1 when narrow

Short and sweet

In a sentence

See all reviews
  • “Setup took one coffee. I was taking bookings before lunch.”

    Rosa Lindgren · Yoga teacher
  • “The reminder texts alone halved our no-shows.”

    Kofi Mensah · Barber
  • “Clients reschedule themselves now. My phone is quiet.”

    Alma Petrovic · Massage therapist
  • “Three rooms, one calendar, zero double bookings.”

    Ethan Cole · Manager, Fernhill Dental
  • “I finally know which services actually fill my week.”

    Nadia Haddad · Brow artist
  • “Deposits at booking changed how Saturdays feel.”

    Luca Bianchi · Tattoo studio owner
  • “Parents book the next lesson before they leave the door.”

    Grace Obi · Piano tutor
  • “The intake form means I walk into every session prepared.”

    Ola Nordin · Personal trainer
import { ArrowRight } from "lucide-react";

const quotes = [
  { quote: "Setup took one coffee. I was taking bookings before lunch.", name: "Rosa Lindgren", role: "Yoga teacher" },
  { quote: "The reminder texts alone halved our no-shows.", name: "Kofi Mensah", role: "Barber" },
  { quote: "Clients reschedule themselves now. My phone is quiet.", name: "Alma Petrovic", role: "Massage therapist" },
  { quote: "Three rooms, one calendar, zero double bookings.", name: "Ethan Cole", role: "Manager, Fernhill Dental" },
  { quote: "I finally know which services actually fill my week.", name: "Nadia Haddad", role: "Brow artist" },
  { quote: "Deposits at booking changed how Saturdays feel.", name: "Luca Bianchi", role: "Tattoo studio owner" },
  { quote: "Parents book the next lesson before they leave the door.", name: "Grace Obi", role: "Piano tutor" },
  { quote: "The intake form means I walk into every session prepared.", name: "Ola Nordin", role: "Personal trainer" },
];

const initials = (name: string) => name.split(" ").map((w) => w[0]).join("").slice(0, 2);

export function TestimonialsCompactList() {
  return (
    <section aria-labelledby="short-title" className="bg-bg px-5 py-12 font-sans sm:px-10 sm:py-20">
      <div className="mx-auto max-w-6xl">
        <div className="mb-10 flex flex-wrap items-end justify-between gap-x-8 gap-y-4">
          <div className="grid gap-3">
            <p className="text-[13px] font-medium text-primary">Short and sweet</p>
            <h2 id="short-title" className="text-3xl font-semibold tracking-tight text-fg sm:text-4xl">In a sentence</h2>
          </div>
          <a href="/reviews" className="inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline">
            See all reviews <ArrowRight className="size-4" aria-hidden />
          </a>
        </div>
        <ul className="grid gap-x-12 md:grid-cols-2">
          {quotes.map((q) => (
            <li key={q.name} className="border-t border-border">
              <figure className="flex items-start gap-3.5 py-[18px]">
                <span className="grid size-7 shrink-0 place-items-center rounded-full bg-primary-soft text-[11px] font-medium text-primary-soft-fg" aria-hidden>{initials(q.name)}</span>
                <div className="grid min-w-0 gap-1">
                  <blockquote className="text-[15px] leading-normal text-fg"><p>“{q.quote}”</p></blockquote>
                  <figcaption className="text-[13px] text-fg-subtle"><span className="font-medium text-fg-muted">{q.name}</span> · {q.role}</figcaption>
                </div>
              </figure>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.