@vibereview/react
v3.1.9
Published
React SDK for VibeReview - Simple, type-safe feedback widgets
Downloads
2,289
Maintainers
Readme
@vibereview/react
Simple, type-safe React SDK for VibeReview - Add beautiful feedback widgets to your React app in minutes.
🎯 Why VibeReview?
The Problem: Your landing page doesn't convert. Visitors don't trust you. No social proof = no sales.
The Solution: Collect feedback in your app. Display the best reviews on your landing page. Watch conversions skyrocket.
await review("feature-id"); // Collect feedback in your app
<Carousel />; // Display best reviews on landing pageWhat you get:
- 🚀 Boost Conversions - Professional review widgets on your landing page
- ⭐️ Social Proof - Real user testimonials that build instant trust
- 🎨 Ultra-Customizable - ScoreBadge, Carousel, Wall of Love, Fomo notifications
- 💬 Smart Collection - Beautiful feedback widgets inside your app
- 📊 Best Reviews First - Automatic filtering to show only top-rated feedback
- 📱 Professional Design - Matches your brand, mobile-ready
What you DON'T need:
- ❌ No manual testimonial collection
- ❌ No design work for review widgets
- ❌ No coding complex layouts
- ❌ No fake reviews
- ❌ No third-party review platforms
📋 Table of Contents
- Why VibeReview?
- How it Works
- Features
- Installation
- Quick Start
- Usage Examples
- Social Proof Components
- Configuration
- Trigger Versioning
- Hook API
- TypeScript Support
- Framework Examples
- Troubleshooting
💡 How it Works
User Action → Your Code → Feedback Widget → Your Dashboard → Landing Page Widget
👆 👨💻 ⭐️ ⭐️ ⭐️ 📊 💰Example:
- User clicks "Export PDF"
- You call
review('export-pdf') - Beautiful rating widget slides in
- User rates 5 stars ⭐️⭐️⭐️⭐️⭐️ and writes "So fast!"
- You see it instantly in your dashboard
- You display the best reviews on your landing page to convert more visitors
That's the entire flow. No complexity. No setup. Just feedback that converts into sales.
🎥 Video Testimonials: If your trigger has video capture enabled, the video recording widget only appears when users give 4 or 5 stars. This ensures you collect video testimonials from happy customers only, maximizing conversion impact on your landing page.
✨ Features
- 🚀 Landing Page Widgets - Ready-to-use social proof components (ScoreBadge, Carousel, Wall of Love, Fomo)
- 💰 Conversion Focused - Automatically display best reviews to maximize trust and sales
- 🎨 Ultra-Customizable - Professional design that matches your brand
- ⚡️ Lightweight - Minimal wrapper around the VibeReview JavaScript SDK
- 🎯 Type-Safe - Full TypeScript support with excellent autocomplete
- 🪝 React Hooks - Modern React API with
useVibeReview() - 🔄 Trigger Versioning - A/B test different widget configurations
- 📊 Promise-Based - Modern async/await API that always resolves
- 🌍 i18n Support - Built-in multilingual support (EN, FR, ES, DE, PT)
- � Mobile-Ready - Responsive design out of the box
📦 Installation
Prerequisites:
- React 16.8+ (Hooks support required)
- A VibeReview account (free signup)
- A project created in the dashboard
- Your
projectIdandapiKey(available in the dashboard)
npm install @vibereview/reactor with yarn:
yarn add @vibereview/reactor with pnpm:
pnpm add @vibereview/react🚀 Quick Start
1. Wrap your app with the Provider
Add this at the root of your React app (get your keys from dashboard).
For Next.js:
// app/layout.tsx
import { VibeReviewProvider } from "@vibereview/react";
export default function RootLayout({ children }) {
return (
<html>
<body>
<VibeReviewProvider
projectId="proj_abc123" // 👈 Your project ID
apiKey="vbr_xyz789" // 👈 Your API key
mode="development" // 👈 Use 'production' when deploying
>
{children}
</VibeReviewProvider>
</body>
</html>
);
}For Standard React (Vite / CRA):
// src/App.tsx
import { VibeReviewProvider } from "@vibereview/react";
export default function App() {
return (
<VibeReviewProvider
projectId="proj_abc123"
apiKey="vbr_xyz789"
mode="development"
>
<YourApp />
</VibeReviewProvider>
);
}💡 Tip: Use
mode="development"while testing locally to see debug logs. Switch tomode="production"(or omit it) when deploying.
2. Create a trigger in your dashboard
Go to your dashboard and create a trigger with:
- Trigger ID:
export-pdf(this is the unique identifier you'll use in your code) - Type: Rating, Survey, or Text feedback
- Question: "How was the PDF export?"
3. Ask for feedback anywhere
import { useVibeReview } from "@vibereview/react";
function ExportButton() {
const { review } = useVibeReview();
const handleExport = async () => {
await exportDocument(); // Your export logic
// ✨ This is where the magic happens
// Use the trigger ID you created in the dashboard
await review("export-pdf"); // Beautiful widget appears!
};
return <button onClick={handleExport}>📄 Export PDF</button>;
}That's it! 🎉 Users click → Widget appears → Best reviews show on landing page → Visitors convert.
No design needed. No forms to build. No database setup. Just one line that drives sales.
💡 Important: The string you pass to
review()(like'export-pdf') is the Trigger ID you created in your dashboard. It's a unique identifier that tells VibeReview which feedback widget to show.
📖 Usage Examples
⚡️ The Simplest Example (Copy & Paste Ready)
import { useVibeReview } from "@vibereview/react";
function FeedbackButton() {
const { review } = useVibeReview();
return (
<button onClick={() => review("my-feature")}>💬 What do you think?</button>
);
}That's it! One line, one function. When users click, they see a beautiful rating widget. Best reviews → Landing page → More conversions. ✨
Note: Make sure you created a trigger with ID
'my-feature'in your dashboard first!
🎯 Real Example: After a Purchase
function CheckoutSuccess() {
const { review } = useVibeReview();
// Ask for feedback right after checkout
useEffect(() => {
review("checkout-experience");
}, []);
return (
<div>
<h1>✅ Order Confirmed!</h1>
<p>Your order #12345 is on its way</p>
</div>
);
}What happens: User completes a purchase → Rating widget appears → Best reviews show on landing page → Future visitors trust you more → Higher conversions.
🚀 Real Example: Feature Launch
function NewExportFeature() {
const { review } = useVibeReview();
const [exported, setExported] = useState(false);
const handleExport = async () => {
// Do the export
await exportToPDF();
setExported(true);
// Ask: "How was it?"
const result = await review("pdf-export");
if (result.completed && result.rating?.rating >= 4) {
// User loves it! Show this review on landing page
console.log("Great review - will boost conversions!");
}
};
return <button onClick={handleExport}>📄 Export to PDF</button>;
}What happens: User tries new feature → Rates 5 stars ⭐️⭐️⭐️⭐️⭐️ → Review appears on landing page → New visitors see social proof → More sign-ups.
💡 Real Example: Smart Retry (User Dismissed)
function SmartFeedback() {
const { review } = useVibeReview();
const askForFeedback = async () => {
const result = await review("onboarding");
if (result.completed) {
// ✅ Got full feedback!
console.log("Rating:", result.rating);
console.log("Comments:", result.feedback);
// Best reviews will appear on landing page automatically
} else if (result.dismissed) {
// ⏭️ User closed it - maybe ask later
setTimeout(() => askForFeedback(), 24 * 60 * 60 * 1000); // Try again tomorrow
}
};
return <button onClick={askForFeedback}>Start Tour</button>;
}What happens: User not ready now? No problem. Ask again later. Every positive review = more conversions on your landing page.
🌍 Real Example: Multi-Language App
function LanguageMenu() {
const { setLocale } = useVibeReview();
return (
<div>
<button onClick={() => setLocale("en")}>🇬🇧 English</button>
<button onClick={() => setLocale("fr")}>🇫🇷 Français</button>
<button onClick={() => setLocale("es")}>🇪🇸 Español</button>
</div>
);
}What happens: User picks their language → Feedback widgets automatically appear in that language. Zero translation work for you.
User Authentication
function ExportButton() {
const { login, review } = useVibeReview();
const [user] = useState({ id: "user_123" }); // Your auth system
// Login user when component mounts
useEffect(() => {
if (user?.id) {
login(user.id, {
name: user.name,
email: user.email,
photoUrl: user.avatarUrl, // Shows on landing page social proof widgets
});
}
}, [user, login]);
const handleExport = async () => {
// 1. Do the actual export
await exportToPDF();
// 2. Then ask for feedback
await review("export-pdf");
// 3. Best reviews automatically appear on landing page 🚀
};
return <button onClick={handleExport}>📄 Export PDF</button>;
}🎨 Social Proof Components for Landing Pages
Once you collect feedback, display it on your landing page to boost conversions. All components are clickable and redirect to your public reviews page (SEO-friendly).
Available Components
import {
ScoreBadge,
AvatarStack,
Carousel,
WallOfLove,
Fomo,
} from "@vibereview/react";Complete Landing Page Example
import { useEffect, useState } from "react";
import {
useVibeReview,
ScoreBadge,
AvatarStack,
Carousel,
WallOfLove,
Fomo,
} from "@vibereview/react";
import type { ProjectInfo } from "@vibereview/react";
export function LandingPage() {
const { getProjectInfo } = useVibeReview();
const [info, setInfo] = useState<ProjectInfo | null>(null);
useEffect(() => {
getProjectInfo().then(setInfo);
}, [getProjectInfo]);
return (
<main>
{/* Hero Section: Show social proof immediately */}
<section className="hero">
<h1>The best tool for X</h1>
<p>Trusted by thousands of users</p>
<AvatarStack /> {/* Shows user avatars with ratings */}
</section>
{/* Testimonials Section: Only show if we have text reviews */}
{info && info.stats.textCount > 0 && (
<section className="testimonials">
<h2>What our users say</h2>
<Carousel /> {/* Sliding carousel of reviews */}
</section>
)}
{/* Wall of Love: Only show if we have enough reviews */}
{info && info.stats.count > 5 && (
<section className="reviews">
<h2>All Reviews</h2>
<WallOfLove /> {/* Masonry/Grid layout */}
</section>
)}
{/* Footer: Trust badge */}
<footer>
<ScoreBadge /> {/* Compact rating badge */}
</footer>
{/* Global: Real-time notifications */}
<Fomo /> {/* "John just rated 5 stars" popup */}
</main>
);
}Component Details
<ScoreBadge />
A compact trust badge showing your average rating and review count.
- Best for: Header, Footer, Sidebar
- Usage:
<ScoreBadge />
<AvatarStack />
A row of user avatars with star rating. High-converting social proof.
- Best for: Hero section, Pricing page, Sign-up forms
- Usage:
<AvatarStack />
<Carousel />
A sliding carousel of your best reviews.
- Best for: Testimonials section, Homepage
- Usage:
<Carousel variant="card" // 'default' | 'card' | 'minimal' autoPlay={true} className="my-10" // Optional: Add margin/padding />
<WallOfLove />
A masonry or grid layout displaying many reviews at once.
- Best for: Dedicated reviews page, Bottom of landing page
- Usage:
<WallOfLove /> - Advanced Options:
<WallOfLove variant="masonry" // 'masonry' | 'grid' | 'compact' contentFilter="all" // 'all' | 'text' | 'video' videoMode="inline" // 'modal' | 'inline' limit={12} theme="light" className="max-w-6xl mx-auto" // Optional container style />
Content Filtering:
contentFilter: 'all'- Show both text and video reviews (default)contentFilter: 'text'- Show only text reviewscontentFilter: 'video'- Show only video testimonials
Video Modes:
videoMode: 'modal'- Click thumbnail to open video in modal (default)videoMode: 'inline'- Play video directly in the card with custom controls
<Fomo />
A small notification popup showing recent reviews in real-time.
- Best for: Global layout (appears in bottom corner)
- Usage:
<Fomo position="bottom-left" variant="rating-only" displayDuration={4000} />
💡 Pro Tip: All components are 100% customizable in your dashboard. Change colors, fonts, layouts, and more - no coding required. They automatically update when new reviews come in.
🎯 JavaScript Usage (No TypeScript)
The package works perfectly with plain JavaScript and still provides autocomplete!
// JavaScript (with autocomplete!)
import { VibeReviewProvider, useVibeReview } from "@vibereview/react";
function App() {
return (
<VibeReviewProvider projectId="proj_xxx" apiKey="vbr_xxx">
<MyApp />
</VibeReviewProvider>
);
}
function MyButton() {
const { review } = useVibeReview();
const handleClick = async () => {
// 'my-trigger' is the Trigger ID from your dashboard
const result = await review("my-trigger");
if (result.completed) {
console.log("Done!");
}
};
return <button onClick={handleClick}>Click me</button>;
}⚙️ Configuration
Provider Props
| Prop | Type | Default | Description |
| ----------- | ------------------------------- | -------------- | ---------------------------------------------------------------- |
| projectId | string | required | Your VibeReview project ID |
| apiKey | string | required | Your VibeReview API key |
| mode | 'development' \| 'production' | 'production' | Environment mode. Use 'development' for debug logs and testing |
| onReady | () => void | - | Called when SDK is loaded and ready |
| onError | (error: Error) => void | - | Called if SDK fails to load |
🔄 Trigger Versioning
VibeReview supports versioning for triggers, allowing you to A/B test different widget configurations without breaking existing implementations.
Why Trigger Versions?
- Test Changes Safely - Try new questions/options without affecting live users
- Gradual Rollout - Roll out changes to a subset of users first
- Rollback Support - Quickly revert to a previous version if needed
- Version History - Track what changed and when
Usage
const { review } = useVibeReview();
// Show latest version (default)
await review("export-pdf");
// Show specific version by name (semantic versioning format)
await review("export-pdf", { version: "2.0.0" });
// Show specific version by number
await review("export-pdf", { version: { number: 2 } });
// Show specific version by explicit name
await review("export-pdf", { version: { name: "2.1.0" } });
// With callbacks AND version
await review("export-pdf", {
onSubmit: () => console.log("Done!"),
version: "2.0.0",
});Version Syntax
| Syntax | Description | Example |
| -------------------------------- | ------------------------------------- | ---------------------------------------- |
| No version | Always use most recent version | Default behavior |
| { version: '2.0.0' } | String = version name (semver format) | '1.0.0', '2.0.0', '2.1.0' |
| { version: { number: 2 } } | Specific version number | { number: 1 }, { number: 2 } |
| { version: { name: '2.1.0' } } | Explicit version name (semver format) | { name: '1.0.0' }, { name: '2.1.0' } |
🪝 Hook API
useVibeReview()
Returns an object with the following methods:
isReady: boolean
Whether the SDK is loaded and ready to use.
const { isReady } = useVibeReview();
return (
<button disabled={!isReady} onClick={() => review("feedback")}>
{isReady ? "Give Feedback" : "Loading..."}
</button>
);login(userId, userInfo?)
Login a user to track their feedback across sessions.
Parameters:
userId(string) - Unique user identifieruserInfo(object, optional) - User metadataname(string) - User's display nameemail(string) - User's emailphotoUrl(string) - URL to user's avatar (shows on landing page widgets)
login("user_123", {
name: "John Doe",
email: "[email protected]",
photoUrl: "https://example.com/avatar.jpg",
});logout()
Logout the current user and clear their session.
logout();review(triggerId, options?): Promise
Display a feedback widget and get the result. The Promise always resolves - it never rejects (except for SDK initialization errors). Check result.completed to see if the user finished the flow.
Parameters:
triggerId(string) - The Trigger ID from your dashboard (e.g.,'export-pdf','checkout-flow')options(object, optional) - Callbacks and/or version optionsonRate(data)- Called when user submits a ratingonFeedback(data)- Called when user submits feedbackonVideoSubmit(data)- Called when user submits a videoonSubmit(data)- Called when entire flow completesonDismiss()- Called when user closes the widgetversion(string | object) - Trigger version- String:
'latest','v1','v2'(treated as version name) - Object:
{ number: 2 }or{ name: 'v2' }
- String:
Returns: Promise
completed(boolean) - True if the entire flow was completeddismissed(boolean, optional) - True if user closed the widgeterror(string, optional) - Error message if something went wrongalreadySubmitted(boolean, optional) - True if user already gave feedbackallVersionsSeen(boolean, optional) - True if user has seen all versionsrating(RatingData, optional) - Rating data if user gave a ratingfeedback(FeedbackData, optional) - Feedback data if user gave feedbacktriggerId(string, optional) - The trigger IDtype('rating' | 'feedback', optional) - Widget type shown
Examples:
// Simple async/await (use your trigger ID from dashboard)
const result = await review("export-pdf");
if (result.completed) {
console.log("Got rating:", result.rating);
console.log("Got feedback:", result.feedback);
}
// Handle different states
const result = await review("checkout-flow");
if (result.completed) {
router.push("/thank-you");
} else if (result.dismissed) {
// User closed it, but we might have partial data
console.log("Partial rating:", result.rating);
scheduleRetry("checkout-flow");
} else if (result.error) {
showError(result.error);
} else if (result.alreadySubmitted) {
showMessage("Thanks for your previous feedback!");
}
// With callbacks (still supported)
const result = await review("onboarding-survey", {
onRate: (data) => {
if (data.rating >= 4) {
// User is happy!
}
},
onSubmit: () => {
// Flow completed
},
});
// With version
const result = await review("feature-x", { version: "v2" });
// With callbacks AND version (same object!)
const result = await review("feature-x", {
onSubmit: () => console.log("Done!"),
version: "v2",
});setLocale(locale)
Set the UI language for the feedback widgets.
Parameters:
locale(string) - Language code: 'en', 'fr', 'es', 'de', 'pt'
setLocale("fr"); // Switch to French
setLocale("es"); // Switch to SpanishgetLocale(): string
Get the current UI language.
Returns: string - Current locale code
const currentLang = getLocale(); // 'en'getAvailableLocales(): string[]
Get list of available languages.
Returns: string[] - Array of locale codes
const languages = getAvailableLocales();
// ['en', 'fr', 'es', 'de', 'pt']setTheme(theme)
Set the UI theme for the feedback widgets.
Parameters:
theme('light' | 'dark' | 'auto') - Theme mode'light'- Force light mode'dark'- Force dark mode'auto'- Follow system preference (default)
setTheme("dark"); // Force dark mode
setTheme("light"); // Force light mode
setTheme("auto"); // Auto-detect from systemgetTheme(): string
Get the current UI theme.
Returns: 'light' | 'dark' | 'auto' - Current theme setting
const currentTheme = getTheme(); // 'auto'getProjectInfo(): Promise<ProjectInfo>
Get information about the project, including statistics and testimonials. Useful for conditionally rendering widgets based on available data.
Returns: Promise<ProjectInfo>
const info = await getProjectInfo();
console.log(info.stats.textCount); // Number of text reviews📘 TypeScript Support
Full TypeScript support with excellent autocomplete:
import type {
RatingData,
FeedbackData,
VideoData,
SubmitData,
ReviewResult,
UserRating,
UserFeedback,
AverageRatingData,
VibeReviewCallbacks,
} from "@vibereview/react";
const handleRate = (data: RatingData) => {
// data.rating is typed as number (1-5)
// data.triggerKey is typed as string
// data.timestamp is typed as number | undefined
};
const handleFeedback = (data: FeedbackData) => {
// data.type is typed as 'text' | 'survey-single' | 'survey-multiple' | 'video'
// data.response is typed as string | string[]
// data.triggerKey is typed as string
};
const handleVideo = (data: VideoData) => {
// data.videoUrl is typed as string
// data.videoThumbnailUrl is typed as string
};
const handleReview = async () => {
const result: ReviewResult = await review("checkout");
// result.completed is typed as boolean
// result.dismissed is typed as boolean | undefined
// result.rating is typed as RatingData | undefined
};All Available Types
// Promise result type
interface ReviewResult {
completed: boolean; // True if entire flow completed
dismissed?: boolean; // True if user closed widget
error?: string; // Error message if something went wrong
alreadySubmitted?: boolean; // True if already gave feedback
allVersionsSeen?: boolean; // True if seen all versions
rating?: RatingData; // Rating data if given
feedback?: FeedbackData; // Feedback data if given
triggerId?: string; // Trigger identifier
type?: "rating" | "feedback"; // Widget type shown
}
// Callback data types
interface RatingData {
rating: number; // 1-5 stars
triggerKey: string; // Trigger identifier
timestamp?: number; // When rated
}
interface FeedbackData {
type: "text" | "survey-single" | "survey-multiple" | "video";
response: string | string[]; // Text or selected option IDs
triggerKey: string;
timestamp?: number;
}
interface VideoData {
videoUrl: string;
videoPath: string;
videoThumbnailUrl: string;
trigger?: any;
}
interface SubmitData {
triggerKey: string;
timestamp?: number;
videoUrl?: string;
videoThumbnailUrl?: string;
}
// API response types
interface UserRating {
rating: number; // 1-5 stars
createdAt?: string; // ISO date string
}
interface UserFeedback {
type: "text" | "survey-single" | "survey-multiple";
response: string | SurveyOption[]; // May be enriched with labels
rating?: number; // If rating was also given
createdAt?: string;
}
interface SurveyOption {
id: string; // Option ID
label: string; // Human-readable label
}
interface AverageRatingData {
averageRating: number; // 0-5 (average)
totalRatings: number; // Count of ratings
}
// User info type
interface UserInfo {
name?: string;
email?: string;
photoUrl?: string;
}🚀 Framework-Specific Examples
Next.js 13+ (App Router)
// app/layout.tsx
import { VibeReviewProvider } from "@vibereview/react";
export default function RootLayout({ children }) {
return (
<html>
<body>
<VibeReviewProvider projectId="..." apiKey="...">
{children}
</VibeReviewProvider>
</body>
</html>
);
}Next.js Pages Router
// pages/_app.tsx
import { VibeReviewProvider } from "@vibereview/react";
function MyApp({ Component, pageProps }) {
return (
<VibeReviewProvider projectId="..." apiKey="...">
<Component {...pageProps} />
</VibeReviewProvider>
);
}Vite + React
// main.tsx
import { VibeReviewProvider } from "@vibereview/react";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<VibeReviewProvider projectId="..." apiKey="...">
<App />
</VibeReviewProvider>
</React.StrictMode>
);🐛 Troubleshooting
"useVibeReview must be used within VibeReviewProvider"
Make sure your component is wrapped with <VibeReviewProvider>.
SDK not loading
Check the browser console for network errors. Ensure your domain is whitelisted in your VibeReview dashboard.
Video Recording not working
Video recording requires a secure context (HTTPS) or localhost. It will not work on insecure HTTP sites due to browser security restrictions on camera access.
TypeScript errors
Make sure you have @types/react installed:
npm install --save-dev @types/react🐛 Troubleshooting
| Issue | Solution |
|-------|----------|
| useVibeReview must be used within VibeReviewProvider | Ensure your component is wrapped with <VibeReviewProvider> |
| Widget not appearing | Verify the triggerId matches the one in your dashboard |
| Video recording not working | Enable HTTPS or use localhost (camera requires secure context) |
| TypeScript errors | Install @types/react and @types/react-dom |
| No data showing | Wait a few minutes after first submission; check your dashboard |
| Authentication issues | Ensure login() is called before review() for logged-in users |
| Widget styling issues | Check for CSS conflicts in your app; use !important if needed |
💡 Best Practices
When to Ask for Feedback
- After successful actions (export, download, purchase)
- After feature usage (try new feature)
- During natural pauses (end of onboarding, between tasks)
- Avoid interrupting critical workflows
Trigger ID Naming Convention
// Good: Descriptive and consistent
review("export-pdf");
review("checkout-success");
review("onboarding-complete");
// Avoid: Vague names
review("feedback-1");
review("popup-3");
## 📄 License
Proprietary © VibeReview - All rights reserved
## 🔗 Links
- [Documentation](https://vibereview.co/docs)
- [Dashboard](https://vibereview.co/dashboard)
- [Support](mailto:[email protected])