Back to library

sections

Testimonials Columns

Vertically scrolling testimonial columns with looping motion. Great for landing page social proof sections.

Inspired by S. Shahaider · View source

Preview

This platform completely changed how our team ships. Everything just clicks.
Alex Rivera
Alex Rivera
Product Lead
The onboarding was so smooth. I had our first workflow live within an hour.
Priya Shah
Priya Shah
Founder, Northloop
Support is fast, docs are clear, and the API feels designed by people who care.
Marcus Chen
Marcus Chen
Staff Engineer
We replaced three separate tools with this. Our stack finally feels sane.
Sara Okonkwo
Sara Okonkwo
Head of Ops
Beautiful defaults, endless flexibility. Exactly what I look for in a tool.
Luca Bianchi
Luca Bianchi
Design Engineer
This platform completely changed how our team ships. Everything just clicks.
Alex Rivera
Alex Rivera
Product Lead
The onboarding was so smooth. I had our first workflow live within an hour.
Priya Shah
Priya Shah
Founder, Northloop
Support is fast, docs are clear, and the API feels designed by people who care.
Marcus Chen
Marcus Chen
Staff Engineer
We replaced three separate tools with this. Our stack finally feels sane.
Sara Okonkwo
Sara Okonkwo
Head of Ops
Beautiful defaults, endless flexibility. Exactly what I look for in a tool.
Luca Bianchi
Luca Bianchi
Design Engineer

Copy for AI

Copy for Lovable is the fastest way to move this component into another Lovable project — it copies the full prompt with dependencies, files, and a usage example. Copy all files gives you the raw source if you want to paste it manually.

Installation

npx shadcn@latest add https://21st.dev/r/sshahaider/testimonials-columns-1

Paste into a terminal — or paste into a Lovable chat and I'll run it for you.

Source files

components/ui/testimonials-columns.tsx
"use client";
import React from "react";
import { motion } from "motion/react";

export type Testimonial = {
  text: string;
  image: string;
  name: string;
  role: string;
};

export const TestimonialsColumn = (props: {
  className?: string;
  testimonials: Testimonial[];
  duration?: number;
}) => {
  return (
    <div className={props.className}>
      <motion.div
        animate={{ translateY: "-50%" }}
        transition={{
          duration: props.duration || 10,
          repeat: Infinity,
          ease: "linear",
          repeatType: "loop",
        }}
        className="flex flex-col gap-6 pb-6 bg-background"
      >
        {[
          ...new Array(2).fill(0).map((_, index) => (
            <React.Fragment key={index}>
              {props.testimonials.map(({ text, image, name, role }, i) => (
                <div
                  className="p-10 rounded-3xl border shadow-lg shadow-primary/10 max-w-xs w-full"
                  key={i}
                >
                  <div>{text}</div>
                  <div className="flex items-center gap-2 mt-5">
                    <img
                      width={40}
                      height={40}
                      src={image}
                      alt={name}
                      className="h-10 w-10 rounded-full"
                    />
                    <div className="flex flex-col">
                      <div className="font-medium tracking-tight leading-5">{name}</div>
                      <div className="leading-5 opacity-60 tracking-tight">{role}</div>
                    </div>
                  </div>
                </div>
              ))}
            </React.Fragment>
          )),
        ]}
      </motion.div>
    </div>
  );
};

Dependencies

motion