website-testimonials
Skill9 starsChanged 30 days agoMITView on GitHub
Encode customer / attendee testimonials as proper schema.org `Review` JSON-LD AND render the "What people say" UI from ONE typed source of truth (`src/data/testimonials.ts`). Individual Review nodes only — author + 5/5 reviewRating + reviewBody + inLanguage + itemReviewed pointing at the SPECIFIC thing reviewed (Event / Product / Service), never a fabricated org-wide AggregateRating. Use when adding quotes, testimonials, social proof, ratings, or star reviews to a site built with the new-website kit (pipeline step 5, alongside `website-seo-geo`). Delegates other schema types to `schema-markup`. Trigger phrases: "add testimonials", "customer quotes", "attendee feedback", "review schema", "star ratings", "social proof section", "encode the quotes", "schema.org Review", "what people say section".
1 other project uses a version of this.
Adopted by
Most recently taken up by
- karero/website-builderword for word, first seen Oct 14, 2026
Other versions1 project uses a version of this.
Word for word · 1
- karero/website-builder★ 9100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository41 other files it gives its agents.
- skills/new-website/templates/AGENTS.mdAGENTS.md
- skills/new-website/templates/CLAUDE.mdCLAUDE.md
- ai-seoSkill
- astro-i18n-setupSkill
- business-listings-setupSkill
- copywritingSkill
- customer-researchSkill
- double-knuthSkill
- facts-checkSkill
- imageSkill
- independent-reviewSkill
- internal-link-auditSkill
- keystatic-setupSkill
- new-websiteSkill
- og-imagesSkill
- outgoing-link-auditSkill
- schema-markupSkill
- search-console-insightsSkill
- search-console-setupSkill
- seo-auditSkill
- seo-repositionSkill
- site-architectureSkill
- website-brand-kitSkill
- website-contact-formSkill
- website-content-guideSkill
- website-design-systemSkill
- website-hero-carouselSkill
- website-launch-checkSkill
- website-motionSkill
- website-new-pageSkill
- website-permissionsSkill
- website-positioning-checkSkill
- website-positioningSkill
- website-qaSkill
- website-reviewSkill
- website-scorecardSkill
- website-seo-geoSkill
- website-storySkill
- website-taglineSkill
- website-team-setupSkill
- wordpress-inspectorSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 41
Quotes are verbatim: Never invent, embellish, or "tidy" a quote or a name.
- Very specific: line 58
Add one entry to src/data/testimonials.ts (set lang.
- Distinctive: line 77
Ship to PREVIEW first, not prod: Put the section on a preview deploy (a branch build, off the production branch) so the team can review …
- Very specific: line 80
Promote per person — ship only the approved subset: If two of three consent, the live TESTIMONIALS array carries those two.
- Very specific: line 82
Withhold, don't delete: Keep an unconsented quote out of the array but leave a // NOTE: comment (the verbatim text also survives in git …
- Very specific: line 154
Every NAMED quote on production has consent.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Website Testimonials & Review Schema
Turn real quotes into (1) a UI section and (2) valid Review structured data,
both from one typed data file so the visible card and the JSON-LD can never
drift. This skill owns the testimonial pattern; it hands generic schema work
(Product, FAQ, Breadcrumb, Article…) to schema-markup.
Read this first — what schema can and can't buy you
- Google does NOT show ⭐ rich-results for self-hosted reviews about your own
Organization/LocalBusiness. It treats them as self-serving and may ignore — or, for a fabricatedAggregateRating, penalise — them. So the value here is truthful semantic markup + GEO (LLMs / AI answer engines citing your social proof), not blue-link stars. Set expectations honestly (Rule 12). itemReviewedis the specific thing the person reviewed — theEvent(talk/workshop),Product,Service,Course— not a blanket org rating. This keeps the markup accurate and is what review-snippet-eligible types want.- Individual
Reviewnodes only by default. NoAggregateRatingunless the reviewed item is a genuinely rich-results-eligible type (e.g.Product) AND the rating reflects all real ratings. Few hand-picked 5-stars + an aggregate reads as spam.
Accuracy rules (non-negotiable)
- Quotes are verbatim. Never invent, embellish, or "tidy" a quote or a name.
- Keep the original language. A quote written in another language stays in
that language (e.g. a German quote on an English page) — translating a
testimonial misrepresents it. Set
langon the element ANDinLanguageon the Review so screen readers + parsers get it right. - Real ratings only.
ratingValueis what the person actually gave.
Sourcing quotes (where they come from)
Testimonials come from wherever real feedback lives: post-purchase emails, post-event surveys, support tickets, sales calls, or review sites (G2, Trustpilot, app stores). Most sources are private to you, with no public feed or API to pull from, so the workflow is manual curation — which is also better editorially, since you pick the strongest, on-message quotes:
- Open the source and copy the best verbatim quote + the rating + the person's name/affiliation.
- Add one entry to
src/data/testimonials.ts(setlang; mapitemName/itemUrlto the specific item reviewed — the product, service, or event). No sync script is possible for most sources — don't build one. - Get consent for named quotes before they go live — see Consent & shipping below.
Example (event sites): Luma keeps ratings organizer-private in Manage Event → Insights, exportable only as CSV; its per-response "share card" is a one-off image, not a live source — so you transcribe by hand. Expect the same of most platforms.
Consent & shipping named quotes
A person left feedback for one purpose, not blanket permission to be quoted by name on your marketing site. Treat consent as a publish gate, applied per person:
- Ask before production. "OK to feature this quote, with your name and affiliation, on our site?" Anonymous/handle-only quotes are lower-risk — but still ask if the wording itself is identifying. Keeps you clean on GDPR/consent.
- Ship to PREVIEW first, not prod. Put the section on a preview deploy (a branch build, off the production branch) so the team can review wording and layout while consent is pending. Keep testimonials off production until approved.
- Promote per person — ship only the approved subset. If two of three
consent, the live
TESTIMONIALSarray carries those two; the third stays out. - Withhold, don't delete. Keep an unconsented quote out of the array but
leave a
// NOTE:comment (the verbatim text also survives in git history), so re-adding on approval is a one-line edit.
Caveat: a static preview is usually not auto-noindexed, and a *.pages.dev
URL is public — so a named, unconsented quote sitting on a preview is still
exposure. Keep previews unlinked and short-lived (or add a branch-only noindex);
the real guarantee is keeping it off production until consent lands.
The data model — one source of truth
src/data/testimonials.ts exports the typed list and the schema builder, so
a page imports both and nothing is duplicated:
import { SITE } from '../config';
export interface Testimonial {
author: string; // name or handle, verbatim
org?: string; // affiliation, shown after the name
rating: number; // 1–5, as actually given
quote: string; // verbatim
lang: string; // BCP-47 tag, e.g. 'en' / 'de' → `lang` attr + inLanguage
itemName: string; // the thing reviewed → itemReviewed.name
itemUrl: string; // canonical URL of that item (absolute)
itemType?: string; // schema.org type of the reviewed thing; default 'Event'
}
// Only consent-approved entries ship here; withhold the rest with a // NOTE.
export const TESTIMONIALS: Testimonial[] = [ /* … */ ];
// Individual Review nodes — no self-serving AggregateRating.
export function reviewSchemas(): Record<string, unknown>[] {
return TESTIMONIALS.map((t) => ({
'@context': 'https://schema.org',
'@type': 'Review',
author: { '@type': 'Person', name: t.author },
reviewRating: { '@type': 'Rating', ratingValue: t.rating, bestRating: 5, worstRating: 1 },
reviewBody: t.quote,
inLanguage: t.lang,
itemReviewed: { '@type': t.itemType ?? 'Event', name: t.itemName, url: t.itemUrl },
}));
}Wiring it in (Astro / new-website kit)
- Schema — pass the builder to
Base'sschemasprop; the Base layout already serialises each entry to<script type="application/ld+json">:
(Do NOT hand-write JSON-LD in the page — go through--- import { TESTIMONIALS, reviewSchemas } from '../data/testimonials'; --- <Base titleExact="…" description="…" schemas={reviewSchemas()}>schemasso it stays in sync with the data file and Base's other schema nodes.) - UI — render cards from the same
TESTIMONIALSarray. Stars are decorative (aria-label="Rated 5 out of 5", the glyphs themselves hidden from the name); foreign-language quotes getlang={t.lang}so AT pronounces them correctly. - Theme — reuse the site's tokens (
--dark-card,--text,--text-muted,--primary-light); a gold star (#F5A623) clears AA on the dark card.
Done means
npm testgreen —a11y.spec.ts(star contrast + lang attrs),tone,seo.- JSON-LD parses and passes the schema.org validator / Google Rich Results Test
with no errors; each
Reviewhasauthor,reviewRating(withbestRating/worstRating),reviewBody,inLanguage, and anitemReviewedwith a realname+ absoluteurl. - The rendered card text equals the
reviewBody(same source — verify one). - No
AggregateRatingunless justified per the rules above. - Every NAMED quote on production has consent; un-approved ones are withheld
(with a
// NOTE:), and anything pending consent shipped to preview only.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

