Blocks

Testimonial 7

standard
1 ColumnText Align CenterAvatar ImageLogosSliderTestimonial Section
Testimonial 7

Code

relume-testimonial7.tsx
"use client";

import { useState, useEffect } from "react";
import React from "react";
import clsx from "clsx";
import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
  type CarouselApi,
} from "@/components/ui/carousel";

type ImageProps = {
  src: string;
  alt?: string;
};

type Testimonial = {
  quote: string;
  logo: ImageProps;
  avatar: ImageProps;
  name: string;
  position: string;
};

type Props = {
  testimonials: Testimonial[];
};

export type Testimonial7Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

export const Testimonial7 = (props: Testimonial7Props) => {
  const { testimonials } = {
    ...Testimonial7Defaults,
    ...props,
  };

  const [api, setApi] = useState<CarouselApi>();
  const [current, setCurrent] = useState(0);

  useEffect(() => {
    if (!api) {
      return;
    }
    setCurrent(api.selectedScrollSnap() + 1);
    api.on("select", () => {
      setCurrent(api.selectedScrollSnap() + 1);
    });
  }, [api]);

  return (
    <section className="overflow-hidden px-[5%] py-16 md:py-24 lg:py-28">
      <div className="container">
        <Carousel
          setApi={setApi}
          opts={{
            loop: true,
            align: "start",
          }}
          className="overflow-hidden"
        >
          <div className="relative">
            <CarouselContent className="ml-0 md:mx-3">
              {testimonials.map((testimonial, index) => (
                <CarouselItem key={index} className="pl-0 md:basis-full md:px-16">
                  <TestimonialCard testimonial={testimonial} />
                </CarouselItem>
              ))}
            </CarouselContent>
            <CarouselPrevious className="mt-8 hidden md:flex" />
            <CarouselNext className="mt-8 hidden md:flex" />
          </div>
          <div className="mt-[30px] flex items-center justify-center md:mt-[46px]">
            {testimonials.map((_, index) => (
              <button
                key={index}
                onClick={() => api?.scrollTo(index)}
                className={clsx("relative mx-[3px] inline-block size-2 rounded-full", {
                  "bg-scheme-text": current === index + 1,
                  "bg-scheme-text/20": current !== index + 1,
                })}
              />
            ))}
          </div>
        </Carousel>
      </div>
    </section>
  );
};

const TestimonialCard = ({ testimonial }: { testimonial: Testimonial }) => {
  return (
    <div className="mx-auto flex w-full max-w-lg flex-col items-center text-center">
      <div className="mb-6 inline-block md:mb-8">
        <img src={testimonial.logo.src} alt={testimonial.logo.alt} className="max-h-14" />
      </div>
      <h5 className="text-h5 font-bold">{testimonial.quote}</h5>
      <div className="mt-6 flex flex-col items-center justify-center md:mt-8">
        <div className="mx-auto mb-3 size-16 min-h-16 min-w-16 overflow-hidden rounded-full md:mb-4">
          <img
            src={testimonial.avatar.src}
            alt={testimonial.avatar.alt}
            className="size-full object-cover"
          />
        </div>
        <div className="flex flex-col items-center justify-center">
          <p className="font-semibold">{testimonial.name}</p>
          <p>{testimonial.position}</p>
        </div>
      </div>
    </div>
  );
};

export const Testimonial7Defaults: Props = {
  testimonials: [
    {
      quote:
        '"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat."',
      logo: {
        src: "https://d22po4pjz3o32e.cloudfront.net/webflow-logo.svg",
        alt: "Webflow logo 1",
      },
      avatar: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg",
        alt: "Testimonial avatar 1",
      },
      name: "Name Surname",
      position: "Position, Company name",
    },
    {
      quote:
        '"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat."',
      logo: {
        src: "https://d22po4pjz3o32e.cloudfront.net/webflow-logo.svg",
        alt: "Webflow logo 2",
      },
      avatar: {
        src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg",
        alt: "Testimonial avatar 2",
      },
      name: "Name Surname",
      position: "Position, Company name",
    },
  ],
};

npm i @radix-ui/react-slot class-variance-authority@0.7.1 clsx@^2.1.1 embla-carousel-react@^8.5.2 motion@^12.15.0 relume-icons@1.3.0 tailwind-merge@^2.2.2