Prism Testimonial
Testimonial cards arranged as a hinged prism — the active quote faces you square on while its neighbours fold away at an angle. Drag, arrow-key or auto-rotate through them, with a spring carrying the turn and the whole assembly tilting slightly toward the pointer.
Included with membership
Members install Prism Testimonial with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.
When to use it
A wall of social proof that should not read as a list — a landing page, a pricing page, an about page. The prism shows one quote properly and hints at the rest, which suits a handful of strong testimonials better than a dozen weak ones. It is a section: give it width, and content of its own above and below.
- Give every item a stable id — it keys the faces, and a changing id snaps the prism mid-turn.
- Use three to seven testimonials. Below three the prism barely reads as one; well above seven the far faces are never seen.
- Keep quotes to a sentence or two. The face is a fixed box, and long copy either shrinks or clips.
- Pass activeIndex with onIndexChange when something else on the page needs to drive the prism; leave both off and it manages itself.
Avoid:
- Do not leave autoRotateInterval running on a page where people are reading. It turns under them mid-sentence — pauseOnHover helps but only once the pointer is already there.
- Do not disable enableDrag, enableKeyboard and showStepControls together; that leaves auto-rotation as the only way through, and no way back.
- Do not raise wingRotationDeg far past the default expecting more drama — past roughly 35 degrees the side faces turn edge-on and disappear.
- Do not rely on the scaling to rescue a genuinely cramped box. It measures its container and shrinks the whole assembly to fit, but it stops at about two-thirds size — below that the side faces start clipping again.
Props
Testimonials arranged around a hinged prism. The one you are reading faces you square on; the others fold away to either side and sit further back. Drag, use the arrow keys, or let it turn on its own — a spring carries the rotation, and the whole assembly leans a little toward the pointer.
<PrismTestimonial />
The prism, its controls and the ticker beneath it. Paints its own background, so treat it as a section.
| Prop | Type | Default | Description |
|---|---|---|---|
items* | TestimonialItem[] | — | The testimonials, in order. Required — nothing renders without it. |
cardWidth | number | 360 | Width of one face, in pixels. |
cardHeight | number | 310 | Height of one face. Quotes are laid out inside this box, so it caps how much copy fits. Scaled down with everything else in a small container. |
cardSpacing | number | 460 | Distance between neighbouring faces along the prism. Wider separates them; narrower overlaps them into a tighter fan. |
depthRecession | number | -130 | How far the side faces sit back from the active one. Negative pushes them away from the viewer — that is what makes it read as depth rather than a row. |
wingRotationDeg | number | 22 | How far the side faces are turned, in degrees. This is the prop that sets the character; past about 35 they go edge-on. |
perspective | string | "1600px" | Depth of the 3D scene as a CSS length. Smaller values exaggerate the perspective, larger ones flatten it. |
parallaxStrength | { pitch?: number; yaw?: number } | { pitch: 3.0, yaw: 4.5 } | Degrees the whole prism leans toward the pointer. Small by design — it is meant to feel alive, not to be noticed. |
springStiffness | number | 32.0 | How hard the prism is pulled toward the next face. |
springDamping | number | 11.5 | How quickly the turn settles. Lower lets it overshoot and rock back. |
maxVelocity | number | 8.5 | Ceiling on rotation speed, so a hard flick cannot spin the prism out of control. |
dragSensitivity | number | 340 | Pixels of drag per face. Higher means you have to pull further to advance. |
activeIndex | number | — | Drives the prism from outside. Supply it and the component becomes controlled — pair it with onIndexChange or it will not move. |
defaultIndex | number | 0 | Which face starts forward when the prism manages its own state. |
onIndexChange | (index: number, item: TestimonialItem) => void | — | Fires whenever a new face comes forward, however it was turned. |
autoRotateInterval | number | 5000 | Milliseconds between automatic turns. Set 0 to leave it still until someone interacts. |
enableDrag | boolean | true | Pointer dragging turns the prism. |
enableKeyboard | boolean | true | Arrow keys turn the prism when it has focus. Leave this on — it is the accessible route through. |
pauseOnHover | boolean | true | Auto-rotation stops while the pointer is over the prism. |
showStepControls | boolean | true | Renders previous and next buttons beneath the prism. |
showRating | boolean | true | Shows the star rating on each face. |
defaultRating | number | 5 | Rating used for any item that does not carry its own. |
showMetrics | boolean | true | Shows the metric value and label at the top of a face, when the item supplies them. |
showAvatars | boolean | true | Shows the author photo in the footer of a face. |
showTicker | boolean | true | Renders a scrolling band of company names beneath the prism. |
tickerSpeed | number | 35 | How fast that band travels. It holds still under reduced motion regardless. |
renderTickerItem | (item: TestimonialItem, state: { isActive: boolean; index: number }) => React.ReactNode | — | Replaces the built-in ticker entry. Receives whether that item is the one currently facing forward, so the band can highlight in step with the prism. |
tickerClassName | string | "" | Applied to the ticker band. |
renderCard | (item: TestimonialItem, state: { isActive: boolean; index: number; offset: number }) => React.ReactNode | — | Replaces the built-in face entirely. Receives whether this face is forward and how far it is from centre, so you can show less on the ones turned away. |
cardBgColor | string | "#ffffff" | Face colour. |
cardBorderRadius | string | "1.25rem" | Corner radius of a face, as any CSS length. |
cardClassName | string | "" | Applied to each face. |
title | string | "Trusted by Engineering Leaders" | Heading above the prism. |
titleClassName | string | "" | Applied to the heading. |
backgroundColor | string | "#f5f4ef" | Colour behind the whole section — a warm off-white by default, not white. |
className | string | "" | Applied to the wrapper. |
style | React.CSSProperties | {} | Inline styles merged onto the wrapper. |
mount | "auto" | "eager" | "auto" | When to start the carousel. The default waits until the page has frame headroom to spare. Set "eager" to start immediately. Note this defaults the opposite way from the components that build a 3D scene: those start eagerly and take "auto" to defer. Both mean the same thing by "auto" — let the runtime pick the moment — the default just matches what each component already did. Neither setting takes it off the shared frame loop or overrides the reduced-motion preference. |
<TestimonialItem />
typeOne testimonial. Everything beyond the quote and its author is optional, and the face adapts to what you supply.
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | Stable key for the face. |
company* | string | — | Company name, also used in the ticker. |
quote* | string | — | The testimonial itself. A sentence or two. |
authorName* | string | — | Who said it. |
authorRole* | string | — | Their title and organisation. |
authorAvatar* | string | — | Author photo. |
logo | string | — | Company mark shown on the face. |
metricValue | string | — | The headline number, such as "-68%". Shown only when showMetrics is on. |
metricLabel | string | — | What the number refers to, such as "Checkout flow". |
rating | number | defaultRating | Overrides the default star rating for this item. |
accentColor | string | — | Brand colour used for this item's metric highlight. |