A drop-in, open-source feedback widget for testing your web app.
▶ Try the live demo — it runs in your browser, nothing is sent anywhere.
One floating Report button. Any visitor — signed in or not — can report a bug / feature / idea by writing in any language, recording a voice note, and attaching a screenshot (the whole page or a zone they drag over). Every report lands as a card in your Notion board, with the page URL and browser captured automatically. Testers never need a Notion account.
Framework-agnostic React on the front, pluggable backends on the back.
import { FeedbackWidget } from 'yenze-feedback';
<FeedbackWidget endpoint="/api/feedback" defaultEmail={user?.email} />- 🌍 Any language — testers type in theirs.
- 🎙️ Voice notes → auto-transcribed & translated to English (OpenAI Whisper).
- ✂️ Screenshots — capture the full screen or drag a zone over the exact problem, right on the page.
- 🗂️ Straight to Notion — one card per report (
Type,Status,Source, screenshot, URL, browser, email). - 🧭 Auto-categorise cards by the page they came from (
App Section). - 🎨 Themeable & translatable — one accent colour, every string overridable.
- 🔌 Bring your own transport — POST to an
endpoint, or hand it your ownonSubmit(e.g.supabase.functions.invoke). - 🪶 Zero CSS deps — self-contained inline styles;
html2canvasis the only (optional) peer.
[ FeedbackWidget ] --multipart--> [ backend core ]
type, text, ├─ upload screenshot/audio → Storage (public URL)
email, url, UA, ├─ transcribe voice note → OpenAI Whisper (→ English)
screenshot, audio └─ create page → Notion board
Copy src/ into your app (or install from GitHub), then mount the widget once —
e.g. in your root layout:
import { FeedbackWidget } from 'yenze-feedback';
export default function Layout({ children }) {
return (
<>
{children}
<FeedbackWidget
endpoint="/api/feedback"
defaultEmail={user?.email} // auto-fills the reporter email
accent="#0f3d2e"
/>
</>
);
}Screenshots need html2canvas:
npm i html2canvasPrefer a custom transport (auth headers, Supabase, etc.)? Skip endpoint and pass onSubmit:
<FeedbackWidget
onSubmit={(fd) => supabase.functions.invoke('submit-feedback', { body: fd }).then(({ error }) => {
if (error) throw error;
})}
/>Pick the adapter that matches your host (all wrap the same core):
| File | Runtime | Storage |
|---|---|---|
server/nextjs/route.ts |
Next.js App Router | Vercel Blob |
server/node/handler.ts |
Any Request→Response runtime (Vercel Fn, Hono, Bun, Cloudflare) |
Supabase Storage |
server/supabase/index.ts |
Supabase Edge (Deno) | Supabase Storage |
See server/README.md for the Notion database schema, the storage bucket, environment variables, and per-page categorisation.
Copy .env.example and fill it in. Only NOTION_TOKEN + NOTION_DATABASE_ID
are required; screenshot storage and voice transcription degrade gracefully if
their keys are missing.
| Prop | Type | Default | Description |
|---|---|---|---|
endpoint |
string |
/api/feedback |
URL the report is POSTed to. |
onSubmit |
(fd: FormData) => Promise<void> |
— | Custom transport; takes precedence over endpoint. |
accent |
string |
#0f3d2e |
Brand accent colour. |
position |
'middle-right' | 'bottom-right' | 'bottom-left' |
middle-right |
Launcher placement. |
types |
string[] |
['🐛 Bug','✨ Feature','💡 Idea'] |
Report categories. |
enableVoice |
boolean |
true |
Show the voice recorder. |
enableScreenshot |
boolean |
true |
Show screenshot capture. |
defaultEmail |
string |
— | Pre-fill the reporter email. |
metadata |
Record<string,string> |
— | Extra fields appended to every report. |
labels |
Partial<FeedbackLabels> |
English | Override any UI string (translate/rebrand). |
open / onOpenChange |
boolean / fn |
— | Control the panel yourself (hides the built-in launcher). |
Hosted: yenze.io/repos/yenze-feedback-demo — try it live, nothing is sent.
Local: a runnable example site (Vite) lives in examples/demo. It uses a mock
transport, so it works with no backend — open the console to see the payload.
npm run demo
# → http://localhost:5173MIT © 2026 Josu Sanz (Yenze)
