All components

Testimonal 01

Testimonal 01 is a React and Tailwind CSS component from Eldora UI, licensed MIT. Copy it and paste it into your project.

Eldora UI MIT other

What it is

Testimonal 01 is a React and Tailwind CSS component from Eldora UI, 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 react-icons/md

Licence

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

Testimonal 01

registry/blocks/testimonal-01/page.tsx
import Image from "next/image"
import { MdOutlineFormatQuote } from "react-icons/md"

import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from "@/registry/blocks/testimonal-01/components/carousel"
import Section from "@/registry/blocks/testimonal-01/components/section"

const companies = [
  {
    name: "Google",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/Google.svg",
  },
  {
    name: "GitHub",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/GitHub.svg",
  },
  {
    name: "Amazon",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/Amazon.svg",
  },
  {
    name: "Netflix",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/Netflix.svg",
  },
  {
    name: "YouTube",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/YouTube.svg",
  },
  {
    name: "Instagram",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/Instagram.svg",
  },
  {
    name: "Spotify",
    url: "https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/Spotify.svg",
  },
]

export default function Testimonal01Page() {
  return (
    <div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
      <div className="w-full max-w-sm md:max-w-3xl">
        <Section
          title="Testimonial Highlight"
          subtitle="What our customers are saying"
        >
          <Carousel>
            <div className="relative mx-auto max-w-2xl">
              <CarouselContent>
                {Array.from({ length: 7 }).map((_, index) => (
                  <CarouselItem key={index}>
                    <div className="p-2 pb-5">
                      <div className="text-center">
                        <MdOutlineFormatQuote className="text-themeDarkGray mx-auto my-4 text-4xl" />
                        <h4 className="text-1xl mx-auto max-w-lg px-10 font-semibold">
                          {getTestimonialQuote(index)}
                        </h4>
                        <div className="mt-8">
                          <Image
                            width={0}
                            height={40}
                            src={companies[index % companies.length].url}
                            alt={`${companies[index % companies.length].name} Logo`}
                            className="mx-auto h-[40px] w-auto"
                          />
                        </div>
                        <div>
                          <h4 className="text-1xl my-2 font-semibold">
                            {getTestimonialName(index)}
                          </h4>
                        </div>
                        <div className="mb-3">
                          <span className="text-themeDarkGray text-sm">
                            {getTestimonialRole(index)}
                          </span>
                        </div>
                      </div>
                    </div>
                  </CarouselItem>
                ))}
              </CarouselContent>
              <div className="from-background pointer-events-none absolute inset-y-0 left-0 h-full w-2/12 bg-gradient-to-r"></div>
              <div className="from-background pointer-events-none absolute inset-y-0 right-0 h-full w-2/12 bg-gradient-to-l"></div>
            </div>
            <div className="hidden md:block">
              <CarouselPrevious />
              <CarouselNext />
            </div>
          </Carousel>
        </Section>
      </div>
    </div>
  )
}

function getTestimonialQuote(index: number): string {
  const quotes = [
    "eldora ui has revolutionized our security testing process. We're now able to identify and address vulnerabilities faster than ever before.",
    "With eldora ui, we've significantly improved our security posture. It's like having an AI-powered ethical hacker working around the clock.",
    "The AI-driven insights from eldora ui have transformed how we approach cybersecurity. It's a game-changer for our platform's security.",
    "eldora ui's automated penetration testing has saved us countless hours and dramatically enhanced our security measures.",
    "Implementing eldora ui was seamless, and the results were immediate. We're now proactively addressing potential security issues before they become threats.",
    "The continuous monitoring capabilities of eldora ui give us peace of mind. We're always one step ahead in protecting our users' data.",
    "eldora ui's compliance mapping feature has streamlined our security audit processes. It's an essential tool for maintaining trust with our users.",
  ]
  return quotes[index % quotes.length]
}

function getTestimonialName(index: number): string {
  const names = [
    "Alex Rivera",
    "Samantha Lee",
    "Raj Patel",
    "Emily Chen",
    "Michael Brown",
    "Linda Wu",
    "Carlos Gomez",
  ]
  return names[index % names.length]
}

function getTestimonialRole(index: number): string {
  const roles = [
    "Head of Cybersecurity",
    "Chief Information Security Officer",
    "VP of Engineering",
    "Security Operations Manager",
    "Director of IT Security",
    "Lead Security Architect",
    "Chief Technology Officer",
  ]
  return roles[index % roles.length]
}
registry/blocks/testimonal-01/components/carousel.tsx
"use client"

import * as React from "react"
import useEmblaCarousel, {
  type UseEmblaCarouselType,
} from "embla-carousel-react"
import { ArrowLeft, ArrowRight } from "lucide-react"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"

type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]

interface CarouselProps {
  opts?: CarouselOptions
  plugins?: CarouselPlugin
  orientation?: "horizontal" | "vertical"
  setApi?: (api: CarouselApi) => void
}

type CarouselContextProps = {
  carouselRef: ReturnType<typeof useEmblaCarousel>[0]
  api: ReturnType<typeof useEmblaCarousel>[1]
  scrollPrev: () => void
  scrollNext: () => void
  canScrollPrev: boolean
  canScrollNext: boolean
} & CarouselProps

const CarouselContext = React.createContext<CarouselContextProps | null>(null)

function useCarousel() {
  const context = React.useContext(CarouselContext)

  if (!context) {
    throw new Error("useCarousel must be used within a <Carousel />")
  }

  return context
}

const Carousel = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(
  (
    {
      orientation = "horizontal",
      opts,
      setApi,
      plugins,
      className,
      children,
      ...props
    },
    ref
  ) => {
    const [carouselRef, api] = useEmblaCarousel(
      {
        ...opts,
        axis: orientation === "horizontal" ? "x" : "y",
      },
      plugins
    )
    const [canScrollPrev, setCanScrollPrev] = React.useState(false)
    const [canScrollNext, setCanScrollNext] = React.useState(false)

    const onSelect = React.useCallback((api: CarouselApi) => {
      if (!api) {
        return
      }

      setCanScrollPrev(api.canScrollPrev())
      setCanScrollNext(api.canScrollNext())
    }, [])

    const scrollPrev = React.useCallback(() => {
      api?.scrollPrev()
    }, [api])

    const scrollNext = React.useCallback(() => {
      api?.scrollNext()
    }, [api])

    const handleKeyDown = React.useCallback(
      (event: React.KeyboardEvent<HTMLDivElement>) => {
        if (event.key === "ArrowLeft") {
          event.preventDefault()
          scrollPrev()
        } else if (event.key === "ArrowRight") {
          event.preventDefault()
          scrollNext()
        }
      },
      [scrollPrev, scrollNext]
    )

    React.useEffect(() => {
      if (!api || !setApi) {
        return
      }

      setApi(api)
    }, [api, setApi])

    React.useEffect(() => {
      if (!api) {
        return
      }

      onSelect(api)
      api.on("reInit", onSelect)
      api.on("select", onSelect)

      return () => {
        api?.off("select", onSelect)
      }
    }, [api, onSelect])

    return (
      <CarouselContext.Provider
        value={{
          carouselRef,
          api,
          opts,
          orientation:
            orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
          scrollPrev,
          scrollNext,
          canScrollPrev,
          canScrollNext,
        }}
      >
        <div
          ref={ref}
          onKeyDownCapture={handleKeyDown}
          className={cn("relative", className)}
          role="region"
          aria-roledescription="carousel"
          {...props}
        >
          {children}
        </div>
      </CarouselContext.Provider>
    )
  }
)
Carousel.displayName = "Carousel"

const CarouselContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const { carouselRef, orientation } = useCarousel()

  return (
    <div ref={carouselRef} className="overflow-hidden">
      <div
        ref={ref}
        className={cn(
          "flex",
          orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
          className
        )}
        {...props}
      />
    </div>
  )
})
CarouselContent.displayName = "CarouselContent"

const CarouselItem = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const { orientation } = useCarousel()

  return (
    <div
      ref={ref}
      role="group"
      aria-roledescription="slide"
      className={cn(
        "min-w-0 shrink-0 grow-0 basis-full",
        orientation === "horizontal" ? "pl-4" : "pt-4",
        className
      )}
      {...props}
    />
  )
})
CarouselItem.displayName = "CarouselItem"

const CarouselPrevious = React.forwardRef<
  HTMLButtonElement,
  React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
  const { orientation, scrollPrev, canScrollPrev } = useCarousel()

  return (
    <Button
      ref={ref}
      variant={variant}
      size={size}
      className={cn(
        "absolute size-8 rounded-full",
        orientation === "horizontal"
          ? "bottom-0 left-1/2 -translate-x-16 translate-y-4"
          : "-top-12 right-1/2 -translate-x-1/2 rotate-90",
        className
      )}
      disabled={!canScrollPrev}
      onClick={scrollPrev}
      {...props}
    >
      <ArrowLeft className="size-4" />
      <span className="sr-only">Previous slide</span>
    </Button>
  )
})
CarouselPrevious.displayName = "CarouselPrevious"

const CarouselNext = React.forwardRef<
  HTMLButtonElement,
  React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
  const { orientation, scrollNext, canScrollNext } = useCarousel()

  return (
    <Button
      ref={ref}
      variant={variant}
      size={size}
      className={cn(
        "absolute size-8 rounded-full",
        orientation === "horizontal"
          ? "right-1/2 bottom-0 translate-x-16 translate-y-4"
          : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
        className
      )}
      disabled={!canScrollNext}
      onClick={scrollNext}
      {...props}
    >
      <ArrowRight className="size-4" />
      <span className="sr-only">Next slide</span>
    </Button>
  )
})
CarouselNext.displayName = "CarouselNext"

export {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
  type CarouselApi,
}
registry/blocks/testimonal-01/components/section.tsx
interface SectionProps {
  id?: string
  title?: string
  subtitle?: string
  description?: string
  children?: React.ReactNode
  className?: string
}

export default function Section({
  id,
  title,
  subtitle,
  description,
  children,
  className,
}: SectionProps) {
  const sectionId = title ? title.toLowerCase().replace(/\s+/g, "-") : id
  return (
    <section id={id || sectionId}>
      <div className={className}>
        <div className="relative container mx-auto max-w-7xl px-4 py-16">
          <div className="mx-auto space-y-4 pb-6 text-center">
            {title && (
              <h2 className="text-primary font-mono text-sm font-medium tracking-wider uppercase">
                {title}
              </h2>
            )}
            {subtitle && (
              <h3 className="mx-auto mt-4 max-w-xs text-3xl font-semibold sm:max-w-none sm:text-4xl md:text-5xl">
                {subtitle}
              </h3>
            )}
            {description && (
              <p className="mx-auto mt-6 max-w-2xl text-lg leading-8 text-slate-600">
                {description}
              </p>
            )}
          </div>
          {children}
        </div>
      </div>
    </section>
  )
}

Similar components