Three Voices

Portrait of a client

Three voices

Iarrivedwantingtobetoldwhattodo.Ileftknowingthatnobodycould,andthatthiswasgoodnews.

Sharon Van Etten

Individual therapy, fourteen months

Hear more of them
About this block

Three VoicesPRO

A rotating testimonial: a portrait on the left and a large serif quote on the right blur out and in together every six seconds, pause on hover, and can be chosen directly from a row of round portrait buttons, with a pill action beneath.

Testimonial50: Three Voices

Three voices in rotation: a portrait cross-fades beside a quote that settles in word by word, on a six-second timer that pauses while the pointer is over the section, with portrait buttons to choose directly.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/testimonial50?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/testimonial50?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/testimonial50.tsx plus the button22 sliding-marker pill and the text1 word stagger it uses.

Quick start

The installed file exports testimonial50Demo alongside the block: the exact props behind the preview above. Spread it to get a working testimonial carousel in one line.

tsx
import { Testimonial50, testimonial50Demo } from "@/components/beste/block/testimonial50";

export default function Page() {
  return <Testimonial50 {...testimonial50Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Testimonial50 } from "@/components/beste/block/testimonial50";

export default function Page() {
  return (
    <Testimonial50
      eyebrow="Three voices"
      voices={[
        {
          quote: "I arrived wanting to be told what to do. I left knowing that nobody could.",
          name: "Sharon Van Etten",
          context: "Individual therapy, fourteen months",
          image: { src: "/people/sharon.jpg", alt: "Portrait of a client" },
        },
      ]}
      button={{ label: "Read all stories", href: "/stories" }}
    />
  );
}

Props

PropTypeDefaultDescription
eyebrowstringSmall-caps line above the quote
voicesVoice[][]The voices; the first is shown on mount
buttonActionButtonPill action under the portrait buttons
classNamestringExtra classes for the outer <section>
ts
type Voice = {
  quote: string;
  name: string;
  context: string;
  image: { src: string; alt: string };
};

type ActionButton = {
  label: string;
  href: string;
  tone?: "primary" | "light" | "dark" | "outline";
};

Behavior notes

  • The rotation pauses while the pointer is anywhere over the section, so a quote cannot change out from under someone who is reading it.
  • It never starts at all when there are fewer than two voices, or when reduced motion is preferred, in which case the first voice simply stays.
  • The quote column carries min-h-[14rem] rising to min-h-[18rem], which reserves the height so a short quote following a long one does not make the page jump.
  • Each quote runs through text1 with trigger="mount" inside AnimatePresence, so every rotation re-plays the word stagger rather than swapping the text in one frame.
  • The attribution waits on 0.3 + quote.split(" ").length * 0.035, the same arithmetic as the stagger, so it lands as the last word of that particular quote settles.
  • The portrait buttons carry aria-pressed and an aria-label with the person's name, while their images use an empty alt, since the button already has its accessible name.
  • Selecting a voice by hand does not stop the timer, only pausing on hover does, so the section keeps moving once the pointer leaves.

More Testimonial blocks

View all Testimonial
PRO

testimonial53

Portrait and Long Quote

One long testimonial beside a portrait: the photograph drifts inside its frame on the left while a serif quote settles in word by word on the right, closed by a ruled row with the speaker's name, context and a sliding-marker pill.

PRO

testimonial51

Single Voice

One centred testimonial: a long serif quote that settles in word by word, the speaker's portrait, name and context beneath it, and a sliding-marker pill.

PRO

testimonial42

Three Quote Grid

A testimonial grid with an eyebrow over a hairline rule, a two-column heading, and three bordered quote cards each closing with an author avatar, name, and role.

PRO

testimonial48

Client Stories

Long-form testimonials told as stories: a serif heading settling in word by word beside the intro and a pill action, then alternating rows that pair a drifting portrait photograph with a story title, a serif quote, the person, and a link to the full account.

PRO

testimonial54

Three Ruled Quotes

Three short testimonials in ruled columns, each a serif quote with a name and context, under a serif heading that settles in word by word and a sliding-marker pill.

FREE

testimonial1

Carousel Testimonials

Full-width swipeable testimonial carousel with autoplay and dot navigation. Perfect for highlighting multiple customer quotes on landing pages.