A lightweight, dead-simple React library for capturing in-app user feedback with automatic metadata collection and optional screenshots.
- Simple integration - Drop in a single component
- Multiple backends - Supabase, Firebase, or any webhook
- Automatic metadata - URL, viewport, browser info, timestamps
- Optional screenshots - Powered by html2canvas
- Customizable UI - CSS variables for theming
- TypeScript - Full type definitions included
- Lightweight - Tree-shakeable, minimal dependencies
npm install react-light-feedbackFor screenshots (optional):
npm install html2canvasFor Supabase provider:
npm install @supabase/supabase-jsFor Firebase provider:
npm install firebaseimport { FeedbackWidget } from 'react-light-feedback';
function App() {
return (
<FeedbackWidget
provider="supabase"
supabaseUrl="https://xxx.supabase.co"
supabaseKey="your-anon-key"
/>
);
}import { FeedbackWidget } from 'react-light-feedback';
function App() {
return (
<FeedbackWidget
provider="firebase"
firebaseConfig={{
apiKey: "your-api-key",
projectId: "your-project-id",
// ... other config
}}
collectionName="feedback" // optional, defaults to "feedback"
/>
);
}import { FeedbackWidget } from 'react-light-feedback';
function App() {
return (
<FeedbackWidget
provider="webhook"
webhookUrl="https://your-api.com/feedback"
webhookHeaders={{ "Authorization": "Bearer token" }} // optional
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
provider |
'supabase' | 'firebase' | 'webhook' |
Required | Backend provider |
theme |
'light' | 'dark' | 'auto' |
'light' |
Widget theme (auto follows system) |
position |
'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' |
'bottom-right' |
Floating button position |
screenshotEnabled |
boolean |
true |
Show screenshot toggle |
triggerText |
string |
'Feedback' |
Button text |
modalTitle |
string |
'Send feedback' |
Modal title |
placeholder |
string |
"What's on your mind?" |
Textarea placeholder |
user |
{ id?, email?, name? } |
- | User context to include |
onSuccess |
() => void |
- | Called on successful submission |
onError |
(error: Error) => void |
- | Called on submission error |
customTrigger |
React.ReactNode |
- | Replace floating button |
disabled |
boolean |
false |
Disable the widget |
| Prop | Type | Default | Description |
|---|---|---|---|
supabaseUrl |
string |
Required | Supabase project URL |
supabaseKey |
string |
Required | Supabase anon key |
tableName |
string |
'feedback' |
Table to insert into |
| Prop | Type | Default | Description |
|---|---|---|---|
firebaseConfig |
object |
Required | Firebase config object |
collectionName |
string |
'feedback' |
Firestore collection |
| Prop | Type | Default | Description |
|---|---|---|---|
webhookUrl |
string |
Required | URL to POST feedback to |
webhookHeaders |
Record<string, string> |
- | Custom headers |
Override these CSS variables to customize the appearance:
:root {
--feedback-primary-color: #0066cc;
--feedback-primary-hover: #0052a3;
--feedback-bg-color: #ffffff;
--feedback-text-color: #1a1a1a;
--feedback-text-secondary: #666666;
--feedback-border-color: #e0e0e0;
--feedback-border-radius: 8px;
--feedback-font-family: -apple-system, BlinkMacSystemFont, sans-serif;
--feedback-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
--feedback-overlay-bg: rgba(0, 0, 0, 0.5);
}Replace the floating button with your own trigger:
<FeedbackWidget
provider="supabase"
supabaseUrl="..."
supabaseKey="..."
customTrigger={<button className="my-button">Give Feedback</button>}
/>Use the useFeedback hook for programmatic control:
import { FeedbackWidget, useFeedback } from 'react-light-feedback';
function MyComponent() {
const { openModal, submitFeedback, isOpen, isSubmitting } = useFeedback();
return (
<button onClick={openModal}>
Open Feedback Form
</button>
);
}
// Wrap your app with FeedbackWidget
function App() {
return (
<FeedbackWidget provider="supabase" supabaseUrl="..." supabaseKey="...">
<MyComponent />
</FeedbackWidget>
);
}The payload sent to your backend:
interface FeedbackPayload {
message: string;
metadata: {
pageUrl: string;
pagePath: string;
viewport: { width: number; height: number };
screen: { width: number; height: number };
userAgent: string;
timezone: string;
language: string;
referrer: string;
timestamp: string;
};
user?: {
id?: string;
email?: string;
name?: string;
};
screenshot?: string; // base64 data URL
}create table feedback (
id uuid default gen_random_uuid() primary key,
message text not null,
metadata jsonb not null,
user_context jsonb,
screenshot text,
created_at timestamp with time zone default now()
);
-- Enable RLS
alter table feedback enable row level security;
-- Allow inserts from anon users
create policy "Allow anonymous inserts"
on feedback for insert
with check (true);rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /feedback/{document} {
allow create: if true;
allow read, update, delete: if false;
}
}
}The webhook receives a POST request with Content-Type: application/json and the FeedbackPayload as the body.
MIT