# Social-Proof-Testimonials-2

> A social proof section that displays expert testimonial cards in a marquee layout

A shadcn/ui block from the [@hexui](https://hexui.sh) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/hexui/social-proof-testimonials-2).

- Page, with a live preview: https://shadcn.exchange/r/hexui/social-proof-testimonials-2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/hexui/social-proof-testimonials-2-light-3aec3cb13fdbed10.png
- Registry item: https://hexui.sh/r/social-proof-testimonials-2.json
- Categories: social-proof
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://hexui.sh/r/social-proof-testimonials-2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `avatar`, `card`

## Files

### `registry/blocks/social-proof/social-proof-testimonials-2/source/app/social-proof/page.tsx`

```tsx
import { SocialProof } from "../../components/social-proof"

export default function Page() {
    return (
        <SocialProof />
    )
}
```

### `registry/blocks/social-proof/social-proof-testimonials-2/source/components/social-proof.tsx`

```tsx
"use client";

import { Marquee } from '@/components/ui/marquee';
import { TestimonialCard, type Testimonial } from './testimonials-card';

const testimonials: Testimonial[] = [
    {
        avatar: "/demo/avatars/avatar-1.svg",
        comment:
            "This solution has completely transformed how our team works. The efficiency gains have been remarkable, and our productivity has increased significantly.",
        name: 'Sarah Anderson',
        role: 'Product Manager',
        company: 'TechFlow Inc.',
    },
    {
        avatar: "/demo/avatars/avatar-2.svg",
        comment:
            "I've been impressed with the seamless integration and exceptional support. It's rare to find a product that delivers on all its promises.",
        name: 'Michael Chen',
        role: 'CEO',
        company: 'CloudX Solutions',
    },
    {
        avatar: "/demo/avatars/avatar-3.svg",
        comment:
            "The results speak for themselves. We've seen a 40% improvement in our workflow efficiency and the team loves the intuitive interface.",
        name: 'Emma Rodriguez',
        role: 'Operations Director',
        company: 'NextGen Systems',
    },
    {
        avatar: "/demo/avatars/avatar-4.svg",
        comment:
            'Outstanding product with incredible attention to detail. The customer service team goes above and beyond to ensure success.',
        name: 'James Wilson',
        role: 'CTO',
        company: 'Digital Innovations',
    },
    {
        avatar: "/demo/avatars/avatar-5.svg",
        comment:
            'From day one, the implementation was smooth and the ROI has exceeded our expectations. This is a game-changer for our organization.',
        name: 'Lisa Zhang',
        role: 'VP of Strategy',
        company: 'Future Corp',
    },
    {
        avatar: "/demo/avatars/avatar-6.svg",
        comment:
            'What sets this apart is the commitment to continuous improvement. Every update brings valuable enhancements that our team immediately benefits from.',
        name: 'David Kim',
        role: 'Head of Development',
        company: 'InnovateTech',
    },
];

// Split testimonials into two rows; the second row scrolls in reverse for a counter-flow effect.
const firstRow = testimonials.slice(0, testimonials.length / 2)
const secondRow = testimonials.slice(testimonials.length / 2)

export function SocialProof() {
    return (
        <section className="flex w-full min-h-dvh items-center overflow-x-hidden bg-background">
            <div className="mx-auto w-full min-w-0 max-w-7xl px-4 py-10 md:px-8 md:py-16">
                {/* Header */}
                <div className="text-center mb-16">
                    <h2 className="text-2xl md:text-3xl font-bold text-foreground mb-2 text-balance">
                        Trusted by Industry Leaders
                    </h2>
                    <p className="text-sm md:text-base text-muted-foreground max-w-xl mx-auto text-pretty">
                        See how companies like yours are achieving remarkable results with our solution
                    </p>
                </div>

                {/* Testimonials Marquee */}
                <div className='flex min-w-0 flex-col gap-2'>
                    <Marquee pauseOnHover className="w-full min-w-0">
                        {firstRow.map((testimonial, index) => (
                            <TestimonialCard
                                key={index}
                                testimonial={testimonial}
                            />
                        ))}
                    </Marquee>
                    <Marquee reverse pauseOnHover className="w-full min-w-0">
                        {secondRow.map((testimonial, index) => (
                            <TestimonialCard
                                key={index}
                                testimonial={testimonial}
                            />
                        ))}
                    </Marquee>
                </div>
            </div>
        </section>
    );
}
```

### `registry/blocks/social-proof/social-proof-testimonials-2/source/components/testimonials-card.tsx`

```tsx
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Card } from '@/components/ui/card';

export interface Testimonial {
    avatar: string;
    comment: string;
    name: string;
    role: string;
    company?: string;
}

interface TestimonialCardProps {
    testimonial: Testimonial;
}

export function TestimonialCard({ testimonial }: TestimonialCardProps) {
    return (
        <Card className="p-4 sm:p-6 max-w-xs sm:max-w-sm flex flex-col">
            {/* Author Info */}
            <div className="flex items-center border-b pb-3 text-xs text-muted-foreground">
                <Avatar className="w-7 h-7 bg-muted-foreground/36 mr-2 inline-block">
                    <AvatarImage src={testimonial.avatar} alt={testimonial.name} />
                    {/* Fallback initials: first letter of each name part (e.g. "Sarah Anderson" -> "SA"). */}
                    <AvatarFallback>{testimonial.name.split(' ').map((n) => n[0]).join('')}</AvatarFallback>
                </Avatar>
                <div className='flex flex-col'>
                    <p className="font-semibold text-foreground text-sm">
                        {testimonial.name}
                    </p>
                    <p className="text-xs text-muted-foreground">
                        {testimonial.role}
                        {testimonial.company && ` • ${testimonial.company}`}
                    </p>
                </div>
            </div>

            {/* Comment */}
            <p className="text-sm text-muted-foreground grow leading-relaxed">
                {testimonial.comment}
            </p>
        </Card>
    );
}
```

### `registry/components/marquee.tsx`

```tsx
import { type ComponentPropsWithoutRef } from "react"

import { cn } from "@/lib/utils"

interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
  /**
   * Optional CSS class name to apply custom styles
   */
  className?: string
  /**
   * Whether to reverse the animation direction
   * @default false
   */
  reverse?: boolean
  /**
   * Whether to pause the animation on hover
   * @default false
   */
  pauseOnHover?: boolean
  /**
   * Content to be displayed in the marquee
   */
  children: React.ReactNode
  /**
   * Whether to animate vertically instead of horizontally
   * @default false
   */
  vertical?: boolean
  /**
   * Number of times to repeat the content
   * @default 4
   */
  repeat?: number
}

export function Marquee({
  className,
  reverse = false,
  pauseOnHover = false,
  children,
  vertical = false,
  repeat = 4,
  style,
  ...props
}: MarqueeProps) {
  // Edge-fade mask applied via CSS mask-image so content fades out at both ends.
  const fadeMask = vertical
    ? "linear-gradient(to bottom, transparent 0%, black 12%, black 88%, transparent 100%)"
    : "linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%)"

  return (
    <div
      {...props}
      style={{
        ...style,
        maskImage: fadeMask,
        WebkitMaskImage: fadeMask,
      }}
      className={cn(
        "group relative flex gap-(--gap) overflow-hidden [--duration:40s] [--gap:12px]",
        vertical ? "flex-col" : "flex-row",
        className
      )}
    >
      {/* Duplicate children N times so the marquee loops seamlessly without a visible gap. */}
      {Array.from({ length: repeat }).map((_, i) => (
        <div
          key={i}
          className={cn(
            "flex shrink-0 justify-around gap-(--gap) motion-reduce:animate-none",
            vertical
              ? "animate-marquee-vertical flex-col"
              : "animate-marquee flex-row",
            reverse && "direction-[reverse]",
            pauseOnHover && "group-hover:paused"
          )}
        >
          {children}
        </div>
      ))}
    </div>
  )
}
```
