# Testimonial 1

> A quote carousel with an avatar, a role and a short headline per review

A shadcn/ui block from the [@shadcnstore](https://www.shadcnstore.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcnstore/testimonial-1).

- Page, with a live preview: https://shadcn.exchange/r/shadcnstore/testimonial-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcnstore/testimonial-1-light-4c62ff933ae3812c.png
- Registry item: https://shadcnstore.com/r/testimonial-1.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnstore.com/r/testimonial-1.json
```

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

## Dependencies

- npm packages: `lucide-react`
- Registry items: `avatar`, `button`, `card`, `carousel`

## Files

### `storage/app/registry-sources/marketing/testimonials/testimonial-1/page.tsx`

```tsx
import { Testimonial1 } from "@/components/blocks/marketing/testimonials/testimonial-1/components/testimonial-1"

export default function Page() {
  return (
    <div className="flex min-h-svh w-full items-center justify-center">
      <div className="w-full">
        <Testimonial1 />
      </div>
    </div>
  )
}
```

### `storage/app/registry-sources/marketing/testimonials/testimonial-1/testimonial-1.tsx`

```tsx
'use client'

import { cn } from '@/lib/utils'
import { Star } from 'lucide-react'
import * as React from 'react'

type EmblaEventType =
  | 'init'
  | 'pointerDown'
  | 'pointerUp'
  | 'scroll'
  | 'select'
  | 'settle'
  | 'destroy'
  | 'reInit'
  | 'resize'

type CarouselApi = {
  scrollPrev: () => void
  scrollNext: () => void
  scrollTo: (index: number) => void
  canScrollPrev: () => boolean
  canScrollNext: () => boolean
  selectedScrollSnap: () => number
  scrollSnapList: () => number[]
  on: (event: EmblaEventType, callback: () => void) => void
  off: (event: EmblaEventType, callback: () => void) => void
}
import { Card, CardContent, CardHeader, CardFooter, CardTitle } from '@/components/ui/card'
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { testimonials } from "@/components/data/testimonial-1-data"

const RatingStars = ({ rating }: { rating: number }) => (
  <div className='flex items-center gap-1'>
    {[...Array(5)].map((_, index) => (
      <Star key={index} className={cn('size-4', index < Math.floor(rating) ? 'fill-foreground' : 'fill-none')} />
    ))}
    <span className='text-muted-foreground ms-2 text-sm'>({rating})</span>
  </div>
)

export function Testimonial1({ className }: { className?: string }) {
  const [api, setApi] = React.useState<CarouselApi | null>(null)
  const [current, setCurrent] = React.useState(0)

  React.useEffect(() => {
    if (!api) return

    const onSelect = () => {
      setCurrent(api.selectedScrollSnap())
    }

    api.on('select', onSelect)
    return () => {
      api.off('select', onSelect)
    }
  }, [api])

  return (
    <section className={cn('py-12 lg:py-20', className)}>
        <div className='mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8'>
        <header className='mb-16 text-center'>
          <h2 className='text-3xl font-bold text-balance md:text-4xl'>Our Clients Review</h2>
        </header>

        <Carousel
          className='w-full'
          setApi={api => {
            // Only update state if api is defined
            if (api) {
              setApi(api)
            } else {
              setApi(null)
            }
          }}
          opts={{
            align: 'start',
            loop: true,
          }}
        >
          <CarouselContent className='-ml-1'>
            {testimonials.map(testimonial => (
              <CarouselItem key={testimonial.id} className='basis-full px-4 last:pe-0 sm:basis-1/2 lg:basis-1/3'>
                <Card className='h-full overflow-hidden border border-border py-6'>
                  <CardHeader className='gap-0 px-6'>
                    <div className='flex items-center gap-4'>
                      <Avatar className='bg-muted size-12'>
                        <AvatarImage src={testimonial.avatar} alt={testimonial.name} className='size-12' />
                        <AvatarFallback className='bg-card'>{testimonial.name[0]}</AvatarFallback>
                      </Avatar>
                      <div>
                        <CardTitle className='text-foreground font-semibold'>{testimonial.name}</CardTitle>
                        <p className='text-muted-foreground text-sm'>{testimonial.role}</p>
                      </div>
                    </div>
                  </CardHeader>
                  <CardContent className='px-6'>
                    <p className='text-muted-foreground text-base'>{testimonial.content}</p>
                  </CardContent>
                  <CardFooter className='bg-transparent border-t-0 px-6 pb-6'>
                    <RatingStars rating={testimonial.rating ?? 0} />
                  </CardFooter>
                </Card>
              </CarouselItem>
            ))}
          </CarouselContent>
          <CarouselPrevious variant='outline' className='hidden cursor-pointer lg:flex' />
          <CarouselNext variant='outline' className='hidden cursor-pointer lg:flex' />
          <div className='mt-8 flex items-center justify-center gap-2'>
            {testimonials.map((_, index) => (
              <Button
                variant='ghost'
                key={index}
                onClick={() => api?.scrollTo(index)}
                className={cn('h-9 px-4 py-2', 
                  'size-2 cursor-pointer rounded-full !p-0 transition-all',
                  current === index ? 'bg-foreground w-6' : 'bg-muted',
                )}
                aria-label={`Go to slide ${index + 1}`}
                aria-current={current === index ? 'true' : 'false'}
              />
            ))}
          </div>
        </Carousel>
      </div>
    </section>
  )
}
export default Testimonial1
```

### `storage/app/registry-sources/marketing/testimonials/testimonial-1/testimonial-1-data.ts`

```ts
export interface Testimonial {
  id: number
  name: string
  role: string
  avatar: string
  content: string
  rating: number
  company?: string
  label?: string
  score?: number
}

export const testimonials: Testimonial[] = [
  {
    id: 1,
    name: 'Sarah Chen',
    role: 'VP of Engineering',
    company: 'Tech Innovators Inc.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=female-1',
    content:
      'This platform revolutionized how we approach product development. The seamless integration between design and development tools has cut our time-to-market by 60%. Our team can now focus on innovation rather than wrestling with technical complexities.',
    rating: 5,
    label: 'Game Changer',
    score: 98,
  },
  {
    id: 2,
    name: 'Marcus Rodriguez',
    role: 'Lead Product Manager',
    company: 'Creative Studios Co.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=male-3',
    content:
      "The development experience is incredibly intuitive and powerful. We've successfully built three major products using this platform, and each one exceeded our expectations in terms of performance and user satisfaction.",
    rating: 5,
    label: 'Excellent Platform',
    score: 97,
  },
  {
    id: 3,
    name: 'Elena Vasquez',
    role: 'Senior UI Designer',
    company: 'Investment Partners LLC',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=female-2',
    content:
      "The component library is exceptional. Clean, accessible, and incredibly well-documented. It's like having a senior designer on your team 24/7.",
    rating: 5,
    label: 'Outstanding Quality',
    score: 96,
  },
  {
    id: 4,
    name: 'David Park',
    role: 'Full Stack Developer',
    company: 'Urban Spaces Ltd.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=male-4',
    content:
      'Outstanding developer experience. The API is thoughtfully designed, documentation is crystal clear, and support response times are incredible.',
    rating: 5,
    label: 'Great Support',
    score: 99,
  },
  {
    id: 5,
    name: 'Priya Sharma',
    role: 'Product Designer',
    company: 'Global Investments Co.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=female-3',
    content:
      'The design system is beautiful and consistent. I can prototype ideas quickly and hand them off to developers with confidence that the implementation will match perfectly.',
    rating: 5,
    label: 'Perfect for Teams',
    score: 100,
  },
  {
    id: 6,
    name: 'James Thompson',
    role: 'Technical Lead',
    company: 'Startup Hub',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=male-1',
    content:
      'After trying countless frameworks, this is the one that finally clicked. The documentation is exceptional and the developer experience is second to none.',
    rating: 4,
    label: 'Highly Recommended',
    score: 95,
  },
  {
    id: 7,
    name: 'Alexandra Mitchell',
    role: 'Senior Frontend Developer',
    company: 'Enterprise Solutions',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=female-4',
    content:
      'This platform has completely transformed our development workflow. The component system is so well-architected that even complex applications feel simple to build.',
    rating: 5,
    label: 'Transforms Workflow',
    score: 98,
  },
  {
    id: 8,
    name: 'Robert Kim',
    role: 'Engineering Manager',
    company: 'Scale Technologies',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=male-2',
    content:
      'We migrated our entire application to this platform in just two weeks. The performance improvements were immediate and our team productivity has doubled.',
    rating: 5,
    label: 'Fast Migration',
    score: 97,
  },
  {
    id: 9,
    name: 'Maria Santos',
    role: 'Full Stack Engineer',
    company: 'Accessibility First Co.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=female-5',
    content:
      'The accessibility features are top-notch. Building inclusive applications has never been easier. Every component follows best practices out of the box.',
    rating: 4,
    label: 'Accessibility Champion',
    score: 96,
  },
  {
    id: 10,
    name: 'Thomas Anderson',
    role: 'Solutions Architect',
    company: 'Cloud Builders Inc.',
    avatar: 'https://notion-avatars.netlify.app/api/avatar?preset=male-5',
    content:
      'Scalability was our biggest concern, but this platform handles enterprise-level complexity with ease. The architecture decisions made here are truly impressive.',
    rating: 5,
    label: 'Enterprise Ready',
    score: 99,
  },
]
```
