# Sample testimonials that never reach production

> A landing page template needs quotes, logos and numbers to look finished, and publishing invented ones is illegal. Mark them as samples, show them in development, and drop them from the production build.

Every SaaS template ships a testimonials section. Most fill it with invented
people: "Sarah J., Head of Growth at Acme: this changed everything." The
template looks finished, the buyer ships it on Friday, and the fake quote is
now a claim their company makes to customers.

Since October 2024 that is not just bad taste. The FTC's rule on consumer
reviews and testimonials (16 CFR Part 465) makes it unlawful to publish a
testimonial from someone who does not exist or did not say it, with civil
penalties per violation. The EU's rules on unfair commercial practices say the
same about fake reviews. Invented customer logos and made-up results ("3x
faster") are the same problem: an endorsement or a factual claim you cannot
back up.

## The pattern

Keep the section, and make the build decide what may be published.

```ts
// src/lib/site.ts
export interface Proof {
  sample?: boolean;
}

export interface Testimonial extends Proof {
  quote: string;
  name: string;
  role: string;
  company: string;
}

export const SHOW_SAMPLES = process.env.NODE_ENV !== "production";

export function publishable<T extends Proof>(
  items: readonly T[],
  showSamples: boolean = SHOW_SAMPLES,
): T[] {
  return items.filter((item) => showSamples || item.sample !== true);
}
```

Template content carries `sample: true`. The section filters its list with
`publishable()`, renders a small "Sample" badge on every sample it shows, and
returns `null` when nothing is left:

```tsx
export function Testimonials({ testimonials }: { testimonials: SiteConfig["testimonials"] }) {
  const quotes = publishable(testimonials.items);
  if (quotes.length === 0) return null;
  // ...each card renders {item.sample ? <SampleBadge /> : null}
}
```

What this buys you:

- In `next dev` the page looks complete, and every invented entry is labelled.
- `next build` inlines `process.env.NODE_ENV` as `"production"`, so samples are
  dropped at build time. There is no runtime flag to forget.
- A real entry is the same object without the flag. Adding the first real
  quote brings the section back in production with no code change.
- The same helper covers logos, stats and social profile links.

## Test it

Make the production behaviour a unit test, so a later "make the page look
fuller" change cannot quietly publish samples:

```ts
it("never publishes a sample in production", () => {
  for (const list of [site.testimonials.items, site.logos.items]) {
    expect(publishable(list, false).some((item) => item.sample)).toBe(false);
  }
});
```

## What counts as real

A real testimonial is a customer's own words, quoted accurately, from someone
who agreed to be quoted. If you paid for it or gave something in return, say
so next to it. A real logo is a customer who agreed to be named. A real number
is one you can show the data for. When in doubt, leave the sample flag on and
let the section stay hidden.

---

Agentic Boilerplate: A Next.js repo your agent already knows. Free during launch, then $99 once.

- Site map for agents: https://agenticboilerplate.com/llms.txt
- Public API: https://agenticboilerplate.com/openapi.json
- Contact: agenticstudio@gmail.com
