All components

Testimonials 3

Testimonials 3 is a React and Tailwind CSS layout component from SmoothUI, licensed MIT. Copy it and paste it into your project.

SmoothUI MIT layout

What it is

Testimonials 3 is a React and Tailwind CSS layout component from SmoothUI, 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.

Open in the catalogue

Five free copies a month. No card.

Needs

npm i lucide-react motion

Licence

This component comes from SmoothUI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.

Testimonials 3

index.tsx
"use client";

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { getAvatarUrl, getTestimonials } from "@/lib/smoothui-data";
import { Star } from "lucide-react";
import { motion, useReducedMotion } from "motion/react";

const testimonials = getTestimonials(4);

export function TestimonialsStars() {
  const shouldReduceMotion = useReducedMotion();
  return (
    <section>
      <div className="py-24">
        <div className="container mx-auto w-full max-w-5xl px-6">
          <motion.div
            animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
            className="mb-12"
            initial={
              shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
            }
            transition={
              shouldReduceMotion
                ? { duration: 0 }
                : { duration: 0.6, ease: [0.22, 1, 0.36, 1] }
            }
          >
            <h2 className="font-semibold text-4xl text-foreground">
              Developer Reviews
            </h2>
            <p className="my-4 text-balance text-lg text-muted-foreground">
              See what the community is saying about SmoothUI. Real feedback
              from developers building amazing user experiences.
            </p>
          </motion.div>

          <motion.div
            animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1 }}
            className="grid 3xl:grid-cols-3 3xl:gap-12 gap-6 lg:grid-cols-2"
            initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0 }}
            transition={
              shouldReduceMotion
                ? { duration: 0 }
                : { delay: 0.2, duration: 0.6, ease: [0.22, 1, 0.36, 1] }
            }
          >
            {testimonials.map((testimonial, index) => (
              <motion.div
                animate={
                  shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
                }
                className="group rounded-2xl border border-transparent px-4 py-3 duration-200 hover:border-border hover:bg-background/50"
                initial={
                  shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 30 }
                }
                key={testimonial.name}
                transition={
                  shouldReduceMotion
                    ? { duration: 0 }
                    : {
                        delay: index * 0.15,
                        duration: 0.5,
                        ease: [0.22, 1, 0.36, 1],
                      }
                }
                whileHover={
                  shouldReduceMotion
                    ? {}
                    : {
                        transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] },
                        y: -4,
                      }
                }
              >
                <motion.div
                  animate={
                    shouldReduceMotion
                      ? { opacity: 1 }
                      : { opacity: 1, scale: 1 }
                  }
                  className="flex gap-1"
                  initial={
                    shouldReduceMotion
                      ? { opacity: 1 }
                      : { opacity: 0, scale: 0.8 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.2,
                          duration: 0.4,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  {Array.from({ length: 5 }).map((_, i) => (
                    <motion.div
                      animate={
                        shouldReduceMotion
                          ? { opacity: 1 }
                          : { opacity: 1, scale: 1 }
                      }
                      initial={
                        shouldReduceMotion
                          ? { opacity: 1 }
                          : { opacity: 0, scale: 0 }
                      }
                      key={`${testimonial.name}-star-${i}`}
                      transition={
                        shouldReduceMotion
                          ? { duration: 0 }
                          : {
                              delay: index * 0.15 + 0.2 + i * 0.05,
                              duration: 0.3,
                              ease: [0.68, -0.55, 0.265, 1.55],
                            }
                      }
                    >
                      <Star
                        className={cn(
                          "size-4 transition-colors duration-200",
                          i < (testimonial.stars || 0)
                            ? "fill-accent stroke-accent"
                            : "fill-primary stroke-border"
                        )}
                      />
                    </motion.div>
                  ))}
                </motion.div>

                <motion.p
                  animate={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
                  }
                  className="my-4 text-foreground"
                  initial={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 10 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.4,
                          duration: 0.4,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  {testimonial.content}
                </motion.p>

                <motion.div
                  animate={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 1, x: 0 }
                  }
                  className="flex items-center gap-2"
                  initial={
                    shouldReduceMotion ? { opacity: 1 } : { opacity: 0, x: -10 }
                  }
                  transition={
                    shouldReduceMotion
                      ? { duration: 0 }
                      : {
                          delay: index * 0.15 + 0.5,
                          duration: 0.3,
                          ease: [0.22, 1, 0.36, 1],
                        }
                  }
                >
                  <Avatar className="size-6 border border-transparent shadow ring-1 ring-foreground/10">
                    <AvatarImage
                      alt={testimonial.name}
                      src={getAvatarUrl(testimonial.avatar, 48)}
                    />
                    <AvatarFallback>
                      {testimonial.name.charAt(0)}
                    </AvatarFallback>
                  </Avatar>
                  <div className="font-medium text-foreground text-sm">
                    {testimonial.name}
                  </div>
                  <span
                    aria-hidden="true"
                    className="size-1 rounded-full bg-foreground/25"
                  />
                  <span className="text-muted-foreground text-sm">
                    {testimonial.role}
                  </span>
                </motion.div>
              </motion.div>
            ))}
          </motion.div>
        </div>
      </div>
    </section>
  );
}

export default TestimonialsStars;

Similar components