A few ways to let customer words do the talking. Replace the quotes, names, and avatars with the real thing.
Featured quote
One centered testimonial with an avatar and role.
“We dropped in a hero and a navbar and shipped the marketing page the same afternoon. It already looked like us.”
Maya Chen
Head of Design, Northline
tsx
"use client";import { cn } from "@/lib/utils";type Testimonials01Props = { className?: string;};export function Testimonials01({ className }: Testimonials01Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto flex w-full max-w-2xl flex-col items-center px-4 py-12 text-center @[32rem]:px-6 @[32rem]:py-16"> <blockquote className="text-balance text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl"> “We dropped in a hero and a navbar and shipped the marketing page the same afternoon. It already looked like us.” </blockquote> <div className="mt-8 flex flex-col items-center gap-3"> <img src="/assets/brand/demos/avatars/avatar-1.png" alt="" className="size-10 rounded-full object-cover" /> <div> <p className="text-sm font-medium tracking-wide text-foreground"> Maya Chen </p> <p className="mt-0.5 text-xs tracking-wide text-muted-foreground"> Head of Design, Northline </p> </div> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add testimonials-01Usage
import { Testimonials01 } from "@/components/blocks/testimonials/testimonials-01"<Testimonials01 />Three up
Three concise quotes in a responsive grid.
“Token-ready blocks — we stopped rebuilding the same sections every launch.”

Jordan Hale
Founder, Relay
“CLI, drop-in, ship. Boring install story in the best way.”

Priya Nair
Eng Manager, Cove
“Light and dark just worked. Time went to product, not palette fights.”

Chris Ortega
Product, Stackwell
tsx
"use client";import { cn } from "@/lib/utils";type Testimonials02Props = { className?: string;};const TESTIMONIALS = [ { quote: "Token-ready blocks — we stopped rebuilding the same sections every launch.", name: "Jordan Hale", role: "Founder, Relay", avatar: "/assets/brand/demos/avatars/avatar-2.png", }, { quote: "CLI, drop-in, ship. Boring install story in the best way.", name: "Priya Nair", role: "Eng Manager, Cove", avatar: "/assets/brand/demos/avatars/avatar-3.png", }, { quote: "Light and dark just worked. Time went to product, not palette fights.", name: "Chris Ortega", role: "Product, Stackwell", avatar: "/assets/brand/demos/avatars/avatar-4.png", },] as const;export function Testimonials02({ className }: Testimonials02Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto w-full max-w-3xl px-4 py-8 @[32rem]:px-6 @[32rem]:py-12"> <ul className="grid grid-cols-1 gap-3 @[40rem]:grid-cols-3"> {TESTIMONIALS.map((item) => ( <li key={item.name} className="flex flex-col rounded-xl bg-muted p-4" > <blockquote className="flex-1 text-xs tracking-wide text-foreground"> “{item.quote}” </blockquote> <div className="mt-4 flex items-center gap-2.5"> <img src={item.avatar} alt="" className="size-7 shrink-0 rounded-full object-cover" /> <div className="min-w-0"> <p className="truncate text-xs font-medium tracking-wide text-foreground"> {item.name} </p> <p className="truncate text-[11px] tracking-wide text-muted-foreground"> {item.role} </p> </div> </div> </li> ))} </ul> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add testimonials-02Usage
import { Testimonials02 } from "@/components/blocks/testimonials/testimonials-02"<Testimonials02 />Marquee
An infinite ticker of compact review cards.
tsx
"use client";import { cn } from "@/lib/utils";type Testimonials03Props = { className?: string;};const TESTIMONIALS = [ { quote: "Shipped our launch site in a day.", name: "Maya Chen", role: "Northline", avatar: "/assets/brand/demos/avatars/avatar-1.png", }, { quote: "Finally blocks that match our tokens.", name: "Jordan Hale", role: "Relay", avatar: "/assets/brand/demos/avatars/avatar-2.png", }, { quote: "CLI install, drop in, done.", name: "Priya Nair", role: "Cove", avatar: "/assets/brand/demos/avatars/avatar-3.png", }, { quote: "Dark mode without the cleanup pass.", name: "Chris Ortega", role: "Stackwell", avatar: "/assets/brand/demos/avatars/avatar-4.png", }, { quote: "Looks custom without the custom bill.", name: "Sam Okonkwo", role: "Fieldkit", avatar: "/assets/brand/demos/avatars/avatar-5.png", },] as const;function QuoteCard({ quote, name, role, avatar,}: (typeof TESTIMONIALS)[number]) { return ( <figure className="flex w-[18rem] shrink-0 flex-col justify-between rounded-xl border border-border bg-background p-5"> <blockquote className="text-sm tracking-wide text-foreground"> “{quote}” </blockquote> <figcaption className="mt-5 flex items-center gap-3"> <img src={avatar} alt="" className="size-8 shrink-0 rounded-full object-cover" /> <div className="min-w-0"> <p className="truncate text-sm font-medium tracking-wide text-foreground"> {name} </p> <p className="truncate text-xs tracking-wide text-muted-foreground"> {role} </p> </div> </figcaption> </figure> );}export function Testimonials03({ className }: Testimonials03Props) { const track = [...TESTIMONIALS, ...TESTIMONIALS, ...TESTIMONIALS]; return ( <section className={cn("w-full overflow-hidden bg-background", className)}> <style>{` @keyframes testimonials-03-scroll { from { transform: translateX(0); } to { transform: translateX(-33.333%); } } `}</style> <div className="flex w-full flex-col gap-5 py-10"> <p className="px-6 text-left text-xs tracking-wide text-muted-foreground"> What teams say after they ship </p> <div className="w-full overflow-hidden" aria-hidden="true"> <div className="flex w-max gap-3 pr-3" style={{ animation: "testimonials-03-scroll 40s linear infinite" }} > {track.map((item, i) => ( <QuoteCard key={`${item.name}-${i}`} {...item} /> ))} </div> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add testimonials-03Usage
import { Testimonials03 } from "@/components/blocks/testimonials/testimonials-03"<Testimonials03 />
