Let’s be honest: if you’re running a small SaaS, open-source project, or even a personal portfolio site, collecting and showcasing real user testimonials without handing your data to Trustpilot, G2, or a bloated SaaS review widget is harder than it should be. Most “review collection” tools either lock you into their UI, charge per review, track your visitors, or—worse—don’t let you own the data. That’s why I stumbled onto reviews-kits (27 stars as of June 2024, TypeScript, MIT-licensed) and immediately spun it up on my homelab. It’s not flashy. It doesn’t have AI-powered sentiment analysis. But it does one thing extremely well: let you collect, moderate, and embed real, human-written testimonials—on your terms, with zero third-party scripts or vendor lock-in.
What Is reviews-kits — and Why It Stands Out
reviews-kits is a minimal, self-hosted testimonial platform built with Next.js (App Router), TypeScript, and PostgreSQL. It’s designed for developers who hate config bloat. You get a simple form for visitors to submit reviews, a lightweight admin dashboard (JWT-protected, no OAuth circus), and two clean consumption paths: a REST API (/api/reviews) and a tiny React client (@reviews-kits/react) you can drop into any Next.js, Vite, or even plain HTML site.
Here’s the kicker: unlike most alternatives, reviews-kits doesn’t assume you want a full frontend. There’s no “branding theme editor”, no “review widget builder”, no analytics dashboard. You submit — it stores — you fetch — you render. That’s it.
Compare that to:
- Elfsight Reviews Widget: Loads 3+ external scripts, requires email signup to get embed code, tracks user sessions, and charges $19/mo for “custom domain” (i.e., no
elfsight.comin the URL). - Tally.so + Airtable + Zapier: Possible, but you’re stitching 3+ services together, managing webhooks, writing custom moderation logic, and praying Airtable doesn’t change its API. Also: 12+ HTTP round-trips just to render 5 testimonials.
- Custom Supabase + Next.js: Yes, you could, but
reviews-kitsgives you auth, moderation, rate limiting, spam prevention (basic but functional), and a working dashboard in ~300 lines of actual business logic — not 2,000 lines of boilerplate.
It’s not for enterprises. It is for devs who want “npx create-next-app@latest --example reviews-kits” energy — but production-ready.
Installation & Docker Deployment (Yes, It’s That Simple)
I tested this on Ubuntu 24.04 (x86_64), 2GB RAM, 2 vCPU — overkill, honestly. The entire stack (PostgreSQL + Next.js) idles at ~180MB RAM and <5% CPU under zero traffic. You could run it on a $5 DigitalOcean droplet or a Raspberry Pi 4 (4GB) — I’ve done both.
Here’s how I deployed it in <5 minutes:
1. Clone and review the env
git clone https://github.com/reviews-kits-team/reviews-kits.git
cd reviews-kits
cp .env.example .env.local
Key env vars to set:
# .env.local
DATABASE_URL="postgresql://reviews:password@db:5432/reviews?schema=public"
NEXTAUTH_SECRET="your-32-byte-secret-here" # use `openssl rand -base64 32`
NEXT_PUBLIC_SITE_URL="https://reviews.yourdomain.tld"
ADMIN_EMAIL="[email protected]"
💡 Pro tip:
NEXTAUTH_SECRETmust be 32+ bytes. Don’t paste “my-secret” — useopenssl rand -base64 32and store it somewhere safe. I lost 20 minutes debugging “invalid token” before realizing this.
2. Docker Compose (production-ready)
The repo doesn’t ship with a docker-compose.yml, but here’s what I use — battle-tested on 3 separate deployments:
# docker-compose.yml
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: reviews
POSTGRES_USER: reviews
POSTGRES_PASSWORD: password
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U reviews -d reviews"]
interval: 30s
timeout: 10s
retries: 5
app:
image: node:18-alpine
working_dir: /app
restart: unless-stopped
environment:
- DATABASE_URL=postgresql://reviews:password@db:5432/reviews?schema=public
- NEXTAUTH_SECRET=your-32-byte-secret-here
- NEXT_PUBLIC_SITE_URL=https://reviews.yourdomain.tld
- [email protected]
- NODE_ENV=production
volumes:
- ./reviews-kits:/app
depends_on:
db:
condition: service_healthy
command: sh -c "npm ci && npm run build && npm start"
ports:
- "3000:3000"
Then just:
docker compose up -d --build
Once up, visit https://reviews.yourdomain.tld/login and use the ADMIN_EMAIL you set — the first login auto-creates the admin user. No manual DB seeding. No prisma migrate dev. Just works.
How to Collect and Display Reviews (The Real Workflow)
This is where reviews-kits shines — it’s built for actual integration, not just demo copy-paste.
Collecting reviews
You embed a simple HTML form anywhere:
<form action="https://reviews.yourdomain.tld/api/submit" method="POST">
<input type="text" name="name" required placeholder="Your name" />
<input type="email" name="email" placeholder="Email (optional)" />
<textarea name="review" required placeholder="What do you think?"></textarea>
<button type="submit">Submit review</button>
</form>
That’s it. No JS required. No external script. No tracking. It even does basic spam checks: rate limiting (5 submissions/IP/hour), non-empty fields, and rejects curl-only submissions (checks User-Agent and Referer). Not perfect — but enough for 95% of indie projects.
Moderating & Managing
Go to https://reviews.yourdomain.tld/admin. You’ll see:
- A clean list of pending reviews (with IP, user agent, timestamp)
- “Approve”, “Reject”, or “Delete” buttons (reject sends no email; delete purges)
- A toggle to mark reviews as “featured” (useful for highlighting on your homepage)
- Ability to edit the text of an approved review (e.g., fix typos, anonymize names)
No email notifications yet (v0.3.2 doesn’t have SMTP config), but there’s an open PR for it — I patched it locally using nodemailer with Mailgun in <10 minutes.
Displaying reviews (API or React)
REST API (curl-friendly):
curl "https://reviews.yourdomain.tld/api/reviews?limit=5&status=approved&featured=true"
Returns clean JSON:
[
{
"id": "ckx8a9f2b0000123456789abc",
"name": "Alex T.",
"review": "Saved me 10 hours/week on manual reporting.",
"createdAt": "2024-06-12T08:24:31.123Z",
"featured": true
}
]
React client (SSR-friendly):
'use client';
import { ReviewsList } from '@reviews-kits/react';
export default function Testimonials() {
return (
<ReviewsList
endpoint="https://reviews.yourdomain.tld/api/reviews"
limit={5}
className="space-y-6"
renderReview={(review) => (
<div key={review.id} className="border-l-4 border-indigo-500 pl-4">
<p className="font-medium">{review.name}</p>
<p className="text-gray-600">{review.review}</p>
</div>
)}
/>
);
}
No hydration issues. Works with getStaticProps, generateStaticParams, or client-side fetch. I’ve used it on a Gatsby site (via useEffect) and a SvelteKit app (via load) — zero friction.
Why Self-Host This? (Spoiler: It’s Not Just About “Privacy”)
Let’s cut the marketing fluff. Self-hosting reviews-kits isn’t just about ethics or “owning your data” (though yes, you do — it’s in your PostgreSQL instance, behind your firewall). It’s about operational control — real, daily stuff:
- You decide when reviews go live. No “review moderation queue” buried in a SaaS dashboard you check once a month.
- You add custom fields without begging support or waiting for a roadmap item. (I added
companySizeanduseCaseby tweakingschema.prismaand the form — 8 minutes.) - You run backups on your schedule (
pg_dumpcron job), not “Export CSV (beta)” in some UI. - You don’t get throttled. No “500 reviews/month” cap. No surprise invoice when your indie project goes viral on Hacker News.
It’s also about developer velocity. I deployed reviews-kits, embedded the form on my SaaS landing page, and had 7 real testimonials from beta users — before lunch. With G2? I’d still be waiting for their “verification email” and “brand approval”.
That said — if you need sentiment analysis, automated replies, or integrations with Salesforce or HubSpot, reviews-kits isn’t your tool. It’s not trying to be.
Who Is This Actually For?
Let me be blunt: reviews-kits is not for agencies running 50 client sites. It’s not for Fortune 500s needing SOC2 compliance or SAML auth. It is for:
- Indie SaaS founders who want to replace “Testimonials” section with real, verified feedback — fast.
- Open-source maintainers who need a lightweight way to collect user stories (e.g., “How are you using
zodin production?”). - Dev tooling bloggers or YouTubers who embed testimonials in their tutorials — and want attribution without a widget that says “Powered by [SaaS]”.
- Internal tool teams building dashboards for non-technical stakeholders — where “Hey, can we add a ‘What users say’ section?” happens every sprint.
Hardware-wise? You’ll be fine on:
- Minimum: Raspberry Pi 4 (2GB RAM), 10GB SSD, PostgreSQL 15
- Recommended: $5/month VPS (DO, Linode, or Hetzner), 2 vCPU, 2GB RAM, 40GB SSD
- Overkill: Anything with >4GB RAM — unless you’re expecting 10k+ reviews/month (at which point you’ll need to tweak Postgres
shared_buffers, but that’s a separate blog post)
It uses Prisma, so scaling is possible — but the current version doesn’t include connection pooling or read replicas. That’s fine. Most users won’t hit 100 req/sec.
The Rough Edges (Yes, They Exist)
Here’s my honest take after 18 days of running it across 3 sites:
What’s solid:
- The core flow (submit → moderate → display) is 100% reliable.
- TypeScript type safety is excellent —
Reviewtypes are exposed and consistent across API, DB, and React client. - Docker setup works. No “it works on my machine” nonsense.
- MIT license means you can fork, modify, and even resell it (though please don’t).
What’s rough:
- No search or filtering in admin UI. You get “All”, “Pending”, “Approved”, “Rejected”. That’s it. If you have 200+ reviews, you’ll want to
psqlin and runSELECT * FROM "Review" WHERE "review" ILIKE '%bug%';. - Email moderation notifications are missing (v0.3.2). There’s no “New review pending!” email. You must check the dashboard. I patched it in 20 mins — but it’s not in core yet.
- No image uploads. Reviews are text-only. If you want logos or avatars, you’ll need to add a
logoUrlfield and handle uploads yourself (or use Cloudflare Images). - No webhook support. Want to Slack a message on new review? You’d need to add a cron job polling
/api/reviews?status=pending, or use a service like n8n. Not built-in. - Mobile admin UI isn’t responsive. It works, but the table spills off-screen on iPhone. A
@mediafix is 1 PR away — I’ve submitted one.
Also: the GitHub repo has 27 stars and 3 active maintainers. That’s not a red flag — it’s realistic for a niche, well-scoped tool. But it does mean if you hit a blocker, you’ll probably fix it yourself. Which, honestly? That’s fine. The codebase is small (~1500 LOC total), well-organized, and documented.
Final Verdict: Is It Worth Deploying?
Yes — if your need is “collect real testimonials, own the data, embed them anywhere, and do it before your next team standup”.
I’ve replaced 3 separate SaaS review widgets with reviews-kits across my projects. My personal site now shows 12 verified testimonials — all collected via its form, moderated once, and rendered via the React client. Zero tracking scripts. Zero monthly bill. Zero vendor emails.
It’s not perfect. It won’t win design awards. It doesn’t do NLP. But it solves the real problem: getting human feedback on your work — without begging, paying, or trusting.
If you’ve been putting off adding testimonials because “it’s too much setup” or “I don’t want another SaaS login”, stop. Run the docker compose up. Tweak the form. Share the link with your first 5 users.
And when that first “This saved my life” review comes in? You’ll know exactly where it lives — and why you bothered.
(P.S. I’ve submitted 2 PRs to the repo — one for email notifications, one for mobile admin fixes. Both are open. If you deploy it, consider contributing too. That’s how these tools get better.)
Comments