Skip to content
WANCRAFT

C.103Typography & Text

Testimonial Rotator

An editorial quote carousel: words blur in one by one, authors swap, and a pausable timer fills the active indicator.

NewReactMotionTailwindintermediate

Overview

Testimonial Rotator sets one quote at a time in large editorial type. A new quote arrives word by word (blur-in, rise or fade), the author's avatar, name and role swap underneath, and the height is reserved for the longest quote so the page never jumps. Autoplay fills the active indicator like a progress bar and pauses on hover, keyboard focus, hidden tabs and when scrolled offscreen; a visible pause button stops it entirely (WCAG 2.2.2). Previous / next buttons, indicator buttons, arrow keys and touch swipes all navigate, and the live region stays silent while rotating but announces each change when the user is in control.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/testimonial-rotator.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/testimonial-rotator.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/testimonial-rotator.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { TestimonialRotator, type Testimonial } from "@/components/ui/testimonial-rotator";

export function TestimonialRotatorExample() {
  const testimonials: Testimonial[] = [
    { id: "1", quote: "The craft shows in the details nobody asked for.", author: "Aina Rahman", role: "Design lead" },
    { id: "2", quote: "We shipped the launch page a week early.", author: "Daniel Okafor", role: "Founder" },
    { id: "3", quote: "Accessible by default, which never happens.", author: "Mei Lin", role: "Front-end engineer" },
  ];

  return (
    <TestimonialRotator items={testimonials} />
  );
}

03Props

PropTypeDefaultDescription
items{ id: string; quote: string; author: string; role?: string; avatar?: string }[]—Required. Testimonials; initials are drawn when avatar (an image URL) is omitted.
autoplaybooleantrueAdvance automatically; pauses on hover, focus, hidden tabs, offscreen and via the pause button.
intervalnumber6000Time each quote is shown, in ms.
effect"blur" | "rise" | "fade""blur"How the words of a new quote arrive.
staggernumber30Delay between words in ms.
align"left" | "center""left"Text alignment.
size"md" | "lg""lg"Quote type size.
quoteMarkbooleantrueLarge accent quotation mark.
accentstring"#FF7448"Indicator, quote mark and focus ring colour.
labelstring"Testimonials"Accessible name of the carousel.
defaultIndexnumber0Quote shown first.
onIndexChange(index: number) => void—Called whenever the visible quote changes.

04Features

  • 01Word-by-word blur-in, rise or fade with a configurable stagger
  • 02Author avatar, name and role swap with the quote; initials when there is no image
  • 03Height reserved for the longest quote — no layout jump between slides
  • 04Autoplay progress drawn in the active indicator, driven by rAF without re-renders
  • 05Pauses on hover, focus, hidden tab and offscreen; visible pause / play button
  • 06Previous / next, indicator buttons, arrow keys and touch swipe
motion · staggermotion · tweeninput · clickinput · keyboardinput · focusinput · hoverinput · drag

05Accessibility

  • A section with aria-roledescription="carousel" and a label; each quote is a group labelled “2 of 5”.
  • The live region is aria-live="off" while rotating and "polite" whenever rotation is paused or the user is navigating, so autoplay never floods screen readers.
  • Rotation stops on keyboard focus and hover, and a visible pause button stops it for good (WCAG 2.2.2).
  • The quote is exposed as plain text once; the animated word spans are aria-hidden.
  • With prefers-reduced-motion quotes swap instantly and autoplay stays off until the user presses play.