DHxWhy

Highlight Testimonial Section

Testimonials 28

About

Highlight testimonial section for React and Tailwind CSS (shadcn/ui) that sweeps a highlight across the key phrase of each customer quote and lifts that phrase into the section headline. It is built to let visitors read why customers are happy in one line before they read the full quote. A testimonial section is a social-proof block that displays customer quotes with names and roles; this one takes testimonials with a quote, a highlight phrase, an author, a role and an optional avatar, wraps the phrase in a mark element, animates the highlight with a background-size sweep and promotes it into the headline with a FLIP translate and scale animation using the Web Animations API. Supports controlled and uncontrolled index, an optional auto-rotate interval that pauses on hover and focus, dot and arrow navigation with arrow keys, carousel ARIA semantics with a polite live headline, light and dark highlight tokens from the shadcn chart palette, and a reduced motion fallback. Use it on landing pages, pricing pages, case studies and onboarding screens. The included demo rotates three sample quotes and makes no requests. Requires React and lucide-react, with no animation library.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Highlight Testimonial Section to your project and fits it to your theme.

Code

How Highlight Testimonial Section is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import * as React from "react";
import { RotateCcw } from "lucide-react";
import { HighlightTestimonialSection } from "@/components/ui/highlight-testimonial-section";
import type { Testimonial } from "@/components/ui/highlight-testimonial-section";

type DemoProps = { autoplay?: boolean; bare?: boolean; index?: number; interval?: number };

const TESTIMONIALS: Testimonial[] = [
  { quote: "We moved three teams over in a week. Onboarding took minutes, not days, and nobody asked for the old tool back.", highlight: "Onboarding took minutes, not days", author: "Maria Lopez", role: "Head of Platform, Acme", avatar: "https://cdn.21st.dev/assets/mirror/1b/1bd1e03a7e1a1548cdab4819240bd550a5daab2767344e0e302833defe91f142.jpg" },
  { quote: "The dashboards finally answer the questions our execs ask. Reports that used to take an afternoon now take a click.", highlight: "Reports that used to take an afternoon now take a click", author: "Jon Park", role: "Data lead, Northwind", avatar: "https://cdn.21st.dev/assets/mirror/d2/d2996fcdda8bb515128ec84aa81468b77bbb31c13b7de0847dc95a5e5b34a90f.jpg" },
  { quote: "Support replied in under ten minutes with an actual fix, twice. That is the main reason we renewed.", highlight: "replied in under ten minutes with an actual fix", author: "Aisha Rahman", role: "CTO, Fernwood", avatar: "https://cdn.21st.dev/assets/mirror/cc/cc21fdbbb78a68b33c08747bc6566e1f1d28f604f191fa5a6ea344e0f7ece19e.jpg" },
];

export default function HighlightTestimonialSectionDemo({ autoplay = true, bare = false, index: preset, interval = 2500 }: DemoProps) {
  const [index, setIndex] = React.useState(preset ?? 0);
  const [run, setRun] = React.useState(0);
  const [motion, setMotion] = React.useState({ ready: false, reduced: true });

  React.useEffect(() => {
    const media = window.matchMedia("(prefers-reduced-motion: reduce)");
    const update = () => setMotion({ ready: true, reduced: media.matches });
    update();
    media.addEventListener("change", update);
    return () => media.removeEventListener("change", update);
  }, []);

  return (
    <main className="flex min-h-svh w-full flex-col items-center justify-center gap-5 bg-background px-5 py-6 text-foreground">
      {!bare && (
        <header className="w-full max-w-[460px]">
          <p className="mb-1 text-[10px] font-medium uppercase tracking-[0.2em] text-muted-foreground">Social proof, one line at a time</p>
          <h1 className="text-[24px] font-semibold leading-tight tracking-tight">The best phrase becomes the headline.</h1>
        </header>
      )}
      <div className="w-full max-w-[460px]">
        <HighlightTestimonialSection key={run} testimonials={TESTIMONIALS} index={index} onIndexChange={setIndex} interval={autoplay && motion.ready && !motion.reduced ? interval : 0} />
      </div>
      {!bare && (
        <footer className="flex w-full max-w-[460px] items-center justify-between text-[10px] text-muted-foreground">
          <span>Sample quotes · hover to pause, arrows to browse</span>
          <button type="button" data-replay onClick={() => { setIndex(0); setRun((previous) => previous + 1); }} className="inline-flex items-center gap-1 rounded px-1 underline decoration-border underline-offset-4 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"><RotateCcw className="size-3" aria-hidden="true" />Replay</button>
        </footer>
      )}
    </main>
  );
}

Dependencies

More by DHxWhy

View all
  • Product Rail Gallery – Fashion Store Hero Section

    Product Rail Gallery – Fashion Store Hero Section

    1.3K
  • Sushi Conveyor Belt Menu – Restaurant Hero Section

    Sushi Conveyor Belt Menu – Restaurant Hero Section

    334
  • Sun Moon Dark Mode Theme Toggle

    Sun Moon Dark Mode Theme Toggle

    279
  • Team Lanyard Rail – Meet the Team Section with Flip Badges

    Team Lanyard Rail – Meet the Team Section with Flip Badges

    195
  • Live Notification Feed

    Live Notification Feed

    151
  • Country Flags Globe

    Country Flags Globe

    133
  • Skateboard Deck Rail Gallery – Skate Shop Hero Section

    Skateboard Deck Rail Gallery – Skate Shop Hero Section

    130
  • Airplane Window Gallery

    Airplane Window Gallery

    122

More in Testimonials

View all
  • Testimonial

    Testimonial

    15
  • Centered Testimonial

    Centered Testimonial

    25
  • testimonial

    testimonial

    834
  • Testimonials Section

    Testimonials Section

    161
  • Testimonials

    Testimonials

    248
  • Staggered Testimonials Grid

    Staggered Testimonials Grid

    150
  • Testimonials Marquee

    Testimonials Marquee

    609
  • Voice Testimonial

    Voice Testimonial

    130