Marque Testimonial
Marque Testimonial is a React and Tailwind CSS layout component from UI Layouts, licensed MIT. Copy it and paste it into your project.
UI Layouts
MIT
layout
What it is
Marque Testimonial is a React and Tailwind CSS layout component from UI Layouts, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i motion
Licence
This component comes from UI Layouts and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Marque Testimonial
'use client'
import React from 'react'
import { motion } from 'motion/react'
import { Star, Quote } from 'lucide-react'
const testimonials = [
{
name: 'Sarah Drasner',
role: 'Staff Engineer',
company: 'Microsoft',
text: 'This changed the way our team builds components. The quality and attention to detail is exceptional.',
avatar: 'SD',
rating: 5,
},
{
name: 'Lee Robinson',
role: 'VP of Developer Experience',
company: 'Vercel',
text: 'The attention to detail in these designs is world-class. Every component feels polished and production-ready.',
avatar: 'LR',
rating: 5,
},
{
name: 'Guillermo Rauch',
role: 'CEO',
company: 'Vercel',
text: 'Stunning aesthetics combined with high performance. Exactly what modern web development needs.',
avatar: 'GR',
rating: 5,
},
{
name: 'Addy Osmani',
role: 'Engineering Manager',
company: 'Google',
text: 'Beautiful, accessible, and ready to go. The perfect foundation for any modern web application.',
avatar: 'AO',
rating: 5,
},
{
name: 'Cassidy Williams',
role: 'CTO',
company: 'Contenda',
text: "I am using this in all my future side projects. It's become my go-to component library.",
avatar: 'CW',
rating: 5,
},
]
export const MarqueeTestimonials = () => {
return (
<section className="relative font-manrope w-full overflow-hidden bg-linear-to-br from-zinc-50 to-white py-16">
{/* Header */}
<div className="max-w-7xl mx-auto px-6 mb-12 text-center">
<h2 className="text-5xl font-bold tracking-tight text-zinc-900 mb-4">
Trusted by Industry Leaders
</h2>
<p className="text-lg text-zinc-600 max-w-2xl mx-auto">
See what top engineers and designers from leading companies are saying
about our work
</p>
</div>
{/* Gradient fades */}
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-linear-to-r from-zinc-50 to-transparent z-10" />
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-linear-to-l from-zinc-50 to-transparent z-10" />
<motion.div
className="flex gap-6 px-6 w-max"
animate={{ x: ['0%', '-50%'] }}
transition={{
duration: 25,
repeat: Infinity,
ease: 'linear',
}}
>
{[...testimonials, ...testimonials].map((testimonial, i) => (
<div
key={i}
className="w-96 shrink-0 p-8 rounded-3xl bg-white border border-zinc-200 shadow-lg hover:shadow-xl transition-all duration-300 flex flex-col gap-6 relative overflow-hidden group"
>
{/* Quote icon */}
<div className="absolute top-4 right-4">
<Quote className="w-12 h-12 fill-zinc-200 stroke-zinc-200" />
</div>
{/* Rating */}
<div className="flex ">
{[...Array(testimonial.rating)].map((_, i) => (
<Star
key={i}
className="w-4 h-4 fill-yellow-400 text-yellow-400"
/>
))}
</div>
{/* Testimonial text */}
<p className="text-zinc-700 leading-relaxed text-pretty font-medium">
{testimonial.text}
</p>
{/* Author info */}
<div className="flex items-center gap-4 mt-auto">
<div className="size-12 rounded-full bg-linear-to-br from-zinc-900 to-zinc-700 flex items-center justify-center text-white font-bold text-sm shadow-md">
{testimonial.avatar}
</div>
<div className="flex-1">
<p className="font-semibold text-zinc-900">
{testimonial.name}
</p>
<p className="text-sm text-zinc-600">{testimonial.role}</p>
<p className="text-xs text-zinc-500">{testimonial.company}</p>
</div>
</div>
</div>
))}
</motion.div>
{/* Second row with opposite direction */}
<motion.div
className="flex gap-6 px-6 w-max mt-6"
animate={{ x: ['-50%', '0%'] }}
transition={{
duration: 30,
repeat: Infinity,
ease: 'linear',
}}
>
{[...testimonials, ...testimonials].map((testimonial, i) => (
<div
key={`second-${i}`}
className="w-96 shrink-0 p-8 rounded-3xl bg-white border border-zinc-200 shadow-lg hover:shadow-xl transition-all duration-300 flex flex-col gap-6 relative overflow-hidden group"
>
{/* Quote icon */}
<div className="absolute top-4 left-4 rotate-180">
<Quote className="w-12 h-12 fill-zinc-200 stroke-zinc-200" />
</div>
{/* Rating */}
<div className="flex justify-end">
{[...Array(testimonial.rating)].map((_, i) => (
<Star
key={i}
className="w-4 h-4 fill-yellow-400 text-yellow-400"
/>
))}
</div>
{/* Testimonial text */}
<p className="text-zinc-700 leading-relaxed text-pretty font-medium">
{testimonial.text}
</p>
{/* Author info */}
<div className="flex items-center gap-4 mt-auto">
<div className="size-12 rounded-full bg-linear-to-br from-zinc-900 to-zinc-700 flex items-center justify-center text-white font-bold text-sm shadow-md">
{testimonial.avatar}
</div>
<div className="flex-1">
<p className="font-semibold text-zinc-900">
{testimonial.name}
</p>
<p className="text-sm text-zinc-600">{testimonial.role}</p>
<p className="text-xs text-zinc-500">{testimonial.company}</p>
</div>
</div>
</div>
))}
</motion.div>
</section>
)
}
import type { Variants } from 'motion/react';
import { type HTMLMotionProps, motion, useInView } from 'motion/react';
import type React from 'react';
type TimelineContentProps<T extends keyof HTMLElementTagNameMap> = {
children?: React.ReactNode;
animationNum: number;
className?: string;
timelineRef: React.RefObject<HTMLElement | null>;
as?: T;
customVariants?: Variants;
once?: boolean;
} & HTMLMotionProps<T>;
export const TimelineAnimation = <T extends keyof HTMLElementTagNameMap = 'div'>({
children,
animationNum,
timelineRef,
className,
as,
customVariants,
once = true,
...props
}: TimelineContentProps<T>) => {
const defaultSequenceVariants = {
visible: (i: number) => ({
filter: 'blur(0px)',
y: 0,
opacity: 1,
transition: {
delay: i * 0.5,
duration: 0.5,
},
}),
hidden: {
filter: 'blur(20px)',
y: 0,
opacity: 0,
},
};
const sequenceVariants = customVariants || defaultSequenceVariants;
const isInView = useInView(timelineRef, {
once,
});
const MotionComponent = motion[as || 'div'] as React.ElementType;
return (
<MotionComponent
initial='hidden'
animate={isInView ? 'visible' : 'hidden'}
custom={animationNum}
variants={sequenceVariants}
className={className}
{...props}
>
{children}
</MotionComponent>
);
};