Testimonial Section
A complete social-proof section with a built-in heading, star ratings, avatars, and three layouts — grid, infinite marquee, and single-quote spotlight carousel.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/testimonial-section.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<TestimonialSection />
Installation
npx shadcn@latest add "https://webberui.com/r/testimonial-section.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/testimonial-section.
Usage
TestimonialSection handles the heading and the layout arrangement, while TestimonialCard is a single testimonial. Switch between the three layouts with variant.
import {
TestimonialSection,
TestimonialCard,
} from "@/components/ui/testimonial-section";
<TestimonialSection
variant="grid"
columns={3}
eyebrow="Social proof"
title="What developers actually say"
description="Thousands of teams already build interfaces faster with WebberUI."
>
<TestimonialCard
rating={5}
quote="Install it and it works; the animation is refined. We replaced our in-house components the day after launch."
name="Lin Yi-hsin"
role="Head of Frontend · Nimbus"
avatar="https://i.pravatar.cc/96?img=1"
/>
<TestimonialCard
rating={5}
quote="Complete types, accessibility done right — the code quality is beyond what I expected."
name="Marco Reyes"
role="Staff Engineer · Fold"
/>
</TestimonialSection>;The three layouts
grid(default): a responsive grid whose cards float up one after another with a stagger as they enter the viewport. Set the maximum column count withcolumns(1–4).marquee: an infinite horizontal marquee that pauses when the cursor hovers it or a child element takes focus; adjust the speed withspeed(pixels per second).spotlight: a centered single-quote spotlight carousel that advances automatically and offers arrow and dot controls; adjust the switching interval withinterval(seconds).
<TestimonialSection variant="marquee" speed={40} title="What customers say">
{/* ...TestimonialCard */}
</TestimonialSection>
<TestimonialSection variant="spotlight" interval={6} title="What customers say">
{/* ...TestimonialCard */}
</TestimonialSection>In the spotlight layout, TestimonialCard automatically enlarges the quote and centers the typesetting; in the other layouts it is presented as a bordered card. Passing a string to avatar treats it as an image URL, and you can also pass a custom node; when it is not provided, a placeholder is generated from the first character of the name.
Props
TestimonialSection
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Put TestimonialCard child components here |
variant | "grid" | "marquee" | "spotlight" | "grid" | Layout style |
eyebrow | React.ReactNode | — | A small label above the title |
title | React.ReactNode | — | The section's main heading |
description | React.ReactNode | — | Explanatory text below the main heading |
columns | number | 3 | Maximum column count for the grid layout (1–4) |
stagger | number | 0.1 | Reveal interval between grid cards (seconds) |
delay | number | 0 | Delay before the reveal starts (seconds) |
once | boolean | true | For grid, play only the first time the element enters the viewport |
speed | number | 40 | Marquee scroll speed (pixels per second) |
interval | number | 6 | Spotlight auto-advance interval (seconds) |
TestimonialCard
| Prop | Type | Default | Description |
|---|---|---|---|
quote | React.ReactNode | — | The testimonial content (the quote) |
name | string | — | Name of the person giving the testimonial |
role | React.ReactNode | — | Job title / company, for example "CTO, Acme" |
avatar | string | React.ReactNode | — | A string is treated as an image URL, or pass a custom node |
rating | number | — | Star rating (0–5); when not passed, it is not shown |
logo | React.ReactNode | — | A node such as a company logo (the grid / marquee layouts) |
How it works
- Layout separated from presentation:
TestimonialSectionpasses the currentvariantdown to the cards through Context, so the layout arrangement (grid, marquee, carousel) and the card visuals each do their own job, and cards adjust their styles automatically byvariant. - Infinite marquee:
xis advanced frame by frame withuseAnimationFrameand wraps back once it passes the width of one set, which forms a seamless loop; the width is measured with aResizeObserverso it updates automatically when the container size or the content changes, and observation is stopped on unmount. The second set of content is visual filler carryingaria-hidden, which keeps assistive technology from announcing it twice. - Spotlight carousel:
AnimatePresencehandles the enter and exit cross-fade plus a directional offset; auto-advance usessetInterval, pausing when the cursor hovers or a child element takes focus, and the timer is cleared on unmount. - Semantic markup: the cards use
figure/blockquote/figcaption, and the star rating is arole="img"with anaria-labelannounced as "rating X out of 5".
Accessibility
- When the user has "reduce motion" enabled at the system level: grid renders statically, marquee degrades into a horizontal list you can scroll by hand, and spotlight stops advancing automatically and switches with a quick fade in and fade out instead — none of them produce a sense of movement.
- The marquee pauses on cursor hover and on keyboard focus, which makes reading and operating the links inside it easier.
- Spotlight offers previous/next arrows and dot buttons (
role="tab",aria-selected), and each slide carries an "X of N" label; the controls have keyboard focus styling.