WebberUI

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.

How to install Pro components →See the plans →

Loading preview…
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.

3
0.1
40
6
<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 with columns (1–4).
  • marquee: an infinite horizontal marquee that pauses when the cursor hovers it or a child element takes focus; adjust the speed with speed (pixels per second).
  • spotlight: a centered single-quote spotlight carousel that advances automatically and offers arrow and dot controls; adjust the switching interval with interval (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

PropTypeDefaultDescription
childrenReact.ReactNodePut TestimonialCard child components here
variant"grid" | "marquee" | "spotlight""grid"Layout style
eyebrowReact.ReactNodeA small label above the title
titleReact.ReactNodeThe section's main heading
descriptionReact.ReactNodeExplanatory text below the main heading
columnsnumber3Maximum column count for the grid layout (1–4)
staggernumber0.1Reveal interval between grid cards (seconds)
delaynumber0Delay before the reveal starts (seconds)
oncebooleantrueFor grid, play only the first time the element enters the viewport
speednumber40Marquee scroll speed (pixels per second)
intervalnumber6Spotlight auto-advance interval (seconds)

TestimonialCard

PropTypeDefaultDescription
quoteReact.ReactNodeThe testimonial content (the quote)
namestringName of the person giving the testimonial
roleReact.ReactNodeJob title / company, for example "CTO, Acme"
avatarstring | React.ReactNodeA string is treated as an image URL, or pass a custom node
ratingnumberStar rating (0–5); when not passed, it is not shown
logoReact.ReactNodeA node such as a company logo (the grid / marquee layouts)

How it works

  • Layout separated from presentation: TestimonialSection passes the current variant down 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 by variant.
  • Infinite marquee: x is advanced frame by frame with useAnimationFrame and wraps back once it passes the width of one set, which forms a seamless loop; the width is measured with a ResizeObserver so 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 carrying aria-hidden, which keeps assistive technology from announcing it twice.
  • Spotlight carousel: AnimatePresence handles the enter and exit cross-fade plus a directional offset; auto-advance uses setInterval, 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 a role="img" with an aria-label announced 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.

On this page