Testimonial
shadcn-extras/testimonialFreeComponent
Animated testimonial carousel with data or custom node slides. Autoplay, arrows, dots, keyboard.
Install it
npx shadcn@latest add https://raw.githubusercontent.com/NayanDevLab/shadcn-extras/dev/public/c/testimonial.jsonSource
1'use client';23import * as React from 'react';4import { motion, AnimatePresence } from 'motion/react';5import { ChevronLeft, ChevronRight } from 'lucide-react';6import { cn } from '@/lib/utils';78export type TestimonialData = {9 id?: React.Key;10 avatar?: string; // img url11 name?: string;12 role?: string;13 quote?: string;14 accent?: string; // e.g. '#3b82f6' or 'rgb(...)'15 // Supply your own node instead of quote card (gets active state + index)16 render?: (ctx: { active: boolean; index: number }) => React.ReactNode;17};1819export type TestimonialCarouselProps = {20 items: TestimonialData[];2122 /** visual variant for default renderer */23 variant?: 'card' | 'minimal';24 /** looping & autoplay */25 loop?: boolean;26 autoplay?: boolean;27 autoplayMs?: number;2829 /** controls */30 showArrows?: boolean;31 showDots?: boolean;32 allowKeyboard?: boolean;3334 /** callbacks */35 initialIndex?: number;36 onIndexChange?: (index: number) => void;3738 /** slot-style class overrides */39 className?: string; // wrapper40 contentClassName?: string; // inner panel41 cardClassName?: string;42 avatarClassName?: string;43 nameClassName?: string;44 roleClassName?: string;45 quoteClassName?: string;4647 /** aria */48 ariaLabel?: string;49};5051export function TestimonialCarousel({52 items,53 variant = 'card',54 loop = true,55 autoplay = false,56 autoplayMs = 5000,57 showArrows = true,58 showDots = true,59 allowKeyboard = true,60 initialIndex = 0,61 onIndexChange,62 className,63 contentClassName,64 cardClassName,65 avatarClassName,66 nameClassName,67 roleClassName,68 quoteClassName,69 ariaLabel = 'Testimonials',70}: TestimonialCarouselProps) {71 const count = items.length;72 const [index, setIndex] = React.useState(73 Math.min(Math.max(initialIndex, 0), Math.max(count - 1, 0))74 );75 const active = ((i: number) => ((i % count) + count) % count)(index); // safe modulo7677 const go = React.useCallback(78 (delta: number) => {79 setIndex((i) => {80 const next = i + delta;81 if (!loop) {82 const clamped = Math.min(Math.max(next, 0), count - 1);83 onIndexChange?.(clamped);84 return clamped;85 }86 const wrapped = ((next % count) + count) % count;87 onIndexChange?.(wrapped);88 return wrapped;89 });90 },91 [count, loop, onIndexChange]92 );9394 // autoplay95 React.useEffect(() => {96 if (!autoplay || count <= 1) return;97 const id = setInterval(() => go(1), autoplayMs);98 return () => clearInterval(id);99// … truncated
What it pulls in
npm packages
Files it writes
More from shadcn-extras
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Image Gridanimated-image-grid | shadcn-extras | Components | Free | no deps · 3 files | |
| Blog Card Fourblog-card-four | shadcn-extras | Components | Free | 1 dep | |
| Blog Card Oneblog-card-one | shadcn-extras | Components | Free | no deps | |
| Blog Card Threeblog-card-three | shadcn-extras | Components | Free | 1 dep | |
| Blog Card Twoblog-card-two | shadcn-extras | Components | Free | 1 dep | |
| Bulb Iconbulb-icon | shadcn-extras | Components | Free | 2 deps | |
| Chevron Stepschevron-steps | shadcn-extras | Components | Free | no deps | |
| Circular Gallerycircular-gallery | shadcn-extras | Components | Free | no deps · 3 files |
