Skip to contentAsheeUIAsheeUI

Testimonials

Attributed quotes in a wall, the words, the person who said them, and the picture that may stand in for them.

A testimonials band is the evidence between the features and the price: a few short quotes, each credited to the person who said it. It is the band where a page stops describing the product and lets somebody else do it.

Reach for it when the quotes are specific and attributed, a named person, a role, a company. A wall of anonymous praise is worse than no wall at all, which is why name is the one field the type requires.

Usage

import { Testimonials } from "@asheeui/web";

<Testimonials
  items={[
    {
      quote: "We cut campaign setup from hours to minutes.",
      name: "Ama Boateng",
      role: "Head of growth, Kora Retail",
    },
  ]}
/>

Each item is a value: a quote, a name, an optional role and an optional picture. The heading block, the spacing and the column options are the ones every marketing band takes, and children renders below the quotes. An item may also state an id, used as its key when a builder can reorder the wall.

The quote and its attribution

quote is the only content in the card, and name is printed under it. role is optional, most founders do not need one, and its line is drawn only when it is given, so an attribution without a role is a complete one rather than a gap.

There is no anonymous variant: the band always draws the attribution, and a quote that arrives without a name (a builder field left empty, a payload that skipped it) renders its words over an empty line, beside an avatar with no initials and no label. A reader then hears a quotation with no author, which is the one thing this band exists to prevent. If you have the words but not the person, they do not belong here.

Length is what goes wrong next. A row of cards shares one height, so the longest quote sets it and its neighbours keep the slack: a four-line quote makes a one-line quote beside it look like a caption. Two sentences is a wall; the quote that is one sentence and one number is the one that gets read.

Pictures and substitutions

Every quote can have a picture, and none of them has to. avatarSrc is the URL handed to the framework's Avatar, which derives the initials of name and shows them when there is no picture at all, or when the picture fails to load. A new avatarSrc is a new picture, so a wall that swaps an item tries again rather than keeping the fallback the previous one earned.

avatarComponent and avatarComponentProps are the substitution seam. avatarComponent is the framework's image component itself, the export from next/image, expo-image or whatever the product already loads pictures with, and avatarComponentProps is what that component needs. The band hands it a filled element: fill and sizes are supplied for it unless those props state width, height or fill, and the props you pass win over the ones the framework supplies.

The substitution reaches the picture and nothing else. An item with no avatarSrc still draws its initials whatever avatarComponent names, so one band mixes pictures and initials without a second code path, which is what makes this seam worth using for a CDN with an allow-list, or for a framework's optimised image, rather than a change at every call site.

Props

The nine options are registered under components.testimonials as columns: 1, columnsMd: 2, columnsLg: 3, gap: "lg", spacing: "lg", background: "none", align: "center", containerSize: "lg" and contained: true, deliberately the same values the feature grid registers, so two adjacent bands line up without either of them stating anything. Each resolves from the instance, then that section, then the registered value, and each of the three counts resolves on its own: columns={2} alone still gives two at md and three at lg.

Two values resolve from outside this band. Each quote is a bordered Card at md density, so its radius follows the application's defaultRadius, and the avatar's fallback circle takes its colour from defaultColor and its radius from defaultRadius. A product that themes those two values themes this band without touching it. items, children, as and the heading, its content, its level (titleAs) and its size (headingSize), are instance-only, because the quotes and the page's outline belong to the page rather than to the design.

Accessibility

  • title renders a level-two heading by default, and each quote is a figure with a blockquote inside it and a figcaption for the attribution, so a reader hears a quoted passage with its author attached rather than a paragraph that happens to sit in quotation marks. titleAs states another level, h1 for the wall whose heading is the page's title, h3 for one nested under a section heading, and headingSize changes the treatment without touching the level.
  • The quotes are one list, so a reader hears how many there are before the first one and can move between them. The <section> the band draws is named by that heading through aria-labelledby, so it is a region a reader can jump to; a band with no title has nothing to be named by and stays unnamed, which is when aria-label is worth passing.
  • The avatar is decorative: it is passed an empty alt, so assistive technology skips it, and the caption's name is what a reader hears once. The initials fallback is hidden for the same reason, the caption already carries the name.
  • Nothing the band draws is focusable, quotes are words, so Tab passes over the wall and the page's only stops remain the ones its other bands supply.

The heading block, the data-not-markup decision and the one-h1 rule are shared by every band, see Marketing sections.