Skip to content
goodtabs UI
BrowseBrowse components

Testimonial spotlight

One quote set large and centered, with the name and role underneath.

npx shadcn@latest add https://ui.goodtabs.co/r/testimonial-spotlight.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/testimonial-spotlight

We stopped arguing about buttons. The whole app now looks like one person made it.
Mira OkaforDesign engineer

One short quote at card size.

With a link

Our designer reviewed the first build and had two comments, both about copy.
Tomas VerhoevenFrontend lead, Halden

A larger line with a case study link in the children slot.

Usage

import { TestimonialSpotlight } from "@/components/testimonial-spotlight"

<TestimonialSpotlight quote="We stopped arguing about buttons." name="Mira Okafor" role="Design engineer" />

Props

PropTypeDefaultDescription
quotestringThe quote, plain text, no quote marks.
namestringWho said it.
rolestringTheir role, one short line.
size"sm" | "default""default"Quote scale. sm fits a card or narrow column.
childrenReactNodeOptional slot under the name, for a link or a logo.

Source

testimonial-spotlight/testimonial-spotlight.tsx
import * as React from "react";
import { cn } from "@/lib/utils";

type TestimonialSpotlightProps = React.ComponentProps<"figure"> & {
  /** The quote, as plain text. No quote marks: the layout says it is a quote. */
  quote: string;
  /** Who said it. */
  name: string;
  /** Their role, one short line. */
  role?: string;
  /** Quote scale. "sm" fits a card or narrow column. */
  size?: "sm" | "default";
  /** Optional slot under the name, for example a logo or a link to the case study. */
  children?: React.ReactNode;
};

const quoteSize = {
  sm: "text-lg sm:text-xl",
  default: "text-2xl sm:text-3xl lg:text-4xl",
};

/**
 * One quote, set large and centered. Use it when a single voice carries a section.
 * Server component.
 */
export function TestimonialSpotlight({
  quote,
  name,
  role,
  size = "default",
  children,
  className,
  ...props
}: TestimonialSpotlightProps) {
  return (
    <figure
      data-slot="testimonial-spotlight"
      className={cn("m-0 mx-auto flex max-w-3xl flex-col items-center gap-6 px-4 py-10 text-center", className)}
      {...props}
    >
      <blockquote className={cn("m-0 leading-snug font-medium tracking-tight text-balance", quoteSize[size])}>
        {quote}
      </blockquote>
      <figcaption className="flex flex-col items-center gap-0.5">
        <span className="text-sm font-medium text-foreground">{name}</span>
        {role && <span className="text-xs text-muted-foreground">{role}</span>}
        {children && <div className="mt-3">{children}</div>}
      </figcaption>
    </figure>
  );
}

For AI agents

Hand your agent this page, the markdown at /components/testimonial-spotlight/prompt.md, or the Copy AI prompt button above.

When to use

  • One strong voice between two sections
  • A case study header
  • When a single line carries the argument

When not to use

  • Several quotes at once, where a wall fits
  • Long paragraphs, which read badly at this size
  • Quotes without permission

Agent notes

  • Server component, a figure with blockquote and figcaption
  • Default size is text-2xl to text-4xl; use sm in cards
  • Text is balanced and centered with max-w-3xl
  • Demo people are fictional; replace with real, permissioned quotes