Please use Desktop to view and interact with components
Page Transitions300mseaseInOut
Testimonial Section
Carousel-style testimonial section with smooth transitions
testimonialcarouselreviewscardsshadcn
Useto navigate between components
Preview
What Our Users Say
Don't just take our word for it - hear from our community
This library has transformed how we build user interfaces. The animations are smooth, professional, and easy to implement.
Sarah Johnson
CEO, TechCorp
This component requires shadcn/ui
This component uses shadcn/ui components. Make sure you have shadcn/ui set up in your project.
- Install shadcn/ui:
npx shadcn-ui@latest init - Install required components based on the imports in the code (e.g.,
npx shadcn-ui@latest add button) - Ensure your
tailwind.config.tsandglobals.cssare configured as per shadcn/ui documentation
Code
TypeScript + React
'use client'
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Quote, ChevronLeft, ChevronRight, Star } from 'lucide-react'
import { Card, CardContent, CardDescription, CardHeader } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
const testimonials = [
{
id: 1,
name: 'Sarah Johnson',
role: 'CEO, TechCorp',
content: 'This library has transformed how we build user interfaces. The animations are smooth, professional, and easy to implement.',
rating: 5,
},
{
id: 2,
name: 'Michael Chen',
role: 'Lead Developer, StartupXYZ',
content: 'Best animation library we\'ve used. The components are production-ready and the documentation is excellent.',
rating: 5,
},
{
id: 3,
name: 'Emily Rodriguez',
role: 'Designer, Creative Studio',
content: 'Beautiful animations that bring our designs to life. Highly recommended for any modern web project.',
rating: 5,
},
]
export function TestimonialSection() {
const [currentIndex, setCurrentIndex] = useState(0)
const nextTestimonial = () => {
setCurrentIndex((prev) => (prev + 1) % testimonials.length)
}
const prevTestimonial = () => {
setCurrentIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length)
}
return (
<div className="w-full px-4 py-16">
<div className="mx-auto max-w-4xl">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="mb-12 text-center"
>
<h2 className="mb-4 text-3xl font-bold sm:text-4xl">What Our Users Say</h2>
<p className="text-sm text-[var(--foreground)]/70 sm:text-base md:text-lg">
Don't just take our word for it - hear from our community
</p>
</motion.div>
<div className="relative">
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
transition={{ duration: 0.3 }}
>
<Card className=" bg-[var(--card-bg)]">
<CardHeader>
<div className="mb-4 flex items-center gap-2">
{[...Array(testimonials[currentIndex].rating)].map((_, i) => (
<motion.div
key={i}
initial={{ scale: 0, rotate: -180 }}
animate={{ scale: 1, rotate: 0 }}
transition={{ delay: i * 0.1, type: 'spring' }}
>
<Star className="h-5 w-5 fill-yellow-400 text-yellow-400" />
</motion.div>
))}
</div>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.2, type: 'spring' }}
className="mb-2 inline-flex"
>
<Quote className="h-6 w-6 text-white" />
</motion.div>
</CardHeader>
<CardContent>
<CardDescription className="mb-6 text-base leading-relaxed sm:text-lg">
{testimonials[currentIndex].content}
</CardDescription>
<div className="flex items-center justify-between">
<div>
<p className="font-semibold">{testimonials[currentIndex].name}</p>
<p className="text-sm text-[var(--foreground)]/60">{testimonials[currentIndex].role}</p>
</div>
</div>
</CardContent>
</Card>
</motion.div>
</AnimatePresence>
<div className="mt-8 flex items-center justify-center gap-4">
<Button
variant="outline"
size="icon"
onClick={prevTestimonial}
className="rounded-full"
>
<ChevronLeft className="h-4 w-4" />
</Button>
<div className="flex gap-2">
{testimonials.map((_, index) => (
<motion.button
key={index}
onClick={() => setCurrentIndex(index)}
className="h-2 rounded-full"
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.9 }}
initial={{ width: 8 }}
animate={{ width: index === currentIndex ? 24 : 8 }}
style={{
backgroundColor: index === currentIndex ? 'rgb(var(--accent))' : 'rgb(var(--border))',
}}
/>
))}
</div>
<Button
variant="outline"
size="icon"
onClick={nextTestimonial}
className="rounded-full"
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</div>
)
}
How to Use
- 1Install Framer Motion:
npm install framer-motion - 2Set up shadcn/ui: Install shadcn/ui components used in this code. Check the imports in the code above and install the required components (e.g.,
npx shadcn-ui@latest add button card) - 3Copy the code from above
- 4Paste it into your project and customize as needed
- 5Colors are customizable via Tailwind CSS classes. The default theme uses dark mode colors defined in your globals.css file