Skip to content

jacksonlatka/feedback-widget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

feedback-widget

Press c anywhere in your app to drop a pin and leave an inline comment. Emails the feedback — with a screenshot of the page and the pin drawn on it — to a configured recipient.

Framework-agnostic: works in Next.js (App Router) and Vite + Vercel serverless. Pluggable email transports: Resend, Formsubmit (zero-config), or Gmail-via-user-OAuth.

Install

npm install github:jacksonlatka/feedback-widget

Peer deps (install if you don't have them): react, react-dom, lucide-react. NextAuth is only required if you use the /next-auth adapter.

Wire up Tailwind v4

The widget uses Tailwind utility classes, so Tailwind needs to see its source. Add to your globals.css:

@import "tailwindcss";
@source "../../node_modules/feedback-widget/src";

(Adjust the relative path to wherever your CSS lives.)

Next.js setup

1. Mount the widget

// src/app/layout.tsx
import { FeedbackWidget } from 'feedback-widget';

<Providers>
  {children}
  <FeedbackWidget appName="My App" accentColor="#2563eb" />
</Providers>

2. Tell Next.js to transpile the package

// next.config.ts
const nextConfig = {
  transpilePackages: ['feedback-widget'],
};

3. Create the API route

Pick a transport (see Transports below). Example with Resend:

// src/app/api/feedback/route.ts
import { createFeedbackHandler, resendTransport } from 'feedback-widget/server';

export const POST = createFeedbackHandler({
  transport: resendTransport({
    apiKey: process.env.RESEND_API_KEY!,
    from: 'feedback@yourdomain.com',
  }),
  recipientEmail: 'you@example.com',
  appName: 'My App',
});

Vite + Vercel serverless setup

1. Mount the widget

// src/main.tsx or wherever your app root lives
import { FeedbackWidget } from 'feedback-widget';

<YourApp />
<FeedbackWidget
  appName="My App"
  accentColor="#2563eb"
  user={currentUser}          // optional; omit to show a "Your email" field
/>

2. Vite doesn't need transpilePackages — it compiles node_modules fine.

3. Create the serverless function

// api/feedback.js  (Vercel serverless, Web-standard Request/Response)
import {
  createFeedbackHandler,
  formsubmitTransport,
} from 'feedback-widget/server';

const handler = createFeedbackHandler({
  transport: formsubmitTransport(),
  recipientEmail: process.env.FEEDBACK_RECIPIENT_EMAIL,
  appName: 'My App',
});

export default handler; // (request: Request) => Promise<Response>

Add a rewrite so /api/feedback hits the function (usually automatic; confirm your vercel.json).

Transports

resendTransport({ apiKey, from })

Send via Resend. 100 emails/day free. Requires a Resend account, an API key, and a verified sender address (or onboarding@resend.dev for testing).

transport: resendTransport({
  apiKey: process.env.RESEND_API_KEY!,
  from: 'feedback@yourdomain.com',
}),

Reply-To is set to the submitter's email automatically.

formsubmitTransport()

Send via Formsubmit. Zero config — no account, no API key. The recipient will get a one-time confirmation email on first use; they click the link and future submissions flow through.

transport: formsubmitTransport(),

Tradeoffs vs Resend: lower deliverability, the From: address is Formsubmit's domain (Reply-To is the submitter), and HTML body is rendered with Formsubmit's default template rather than your styled version. Fine for early prototypes, swap to Resend once the project matures.

gmailUserTokenTransport({ getAccessToken })

Send via the signed-in user's Gmail account. Requires the consumer to have OAuth + the gmail.send scope wired up.

transport: gmailUserTokenTransport({
  getAccessToken: async (request) => mySession?.accessToken,
}),

The email's From: is the submitter themselves, so replies naturally go to the right person.

NextAuth convenience (feedback-widget/next-auth)

import { createFeedbackHandler } from 'feedback-widget/server';
import {
  nextAuthGmailTransport,
  nextAuthGetUser,
} from 'feedback-widget/next-auth';
import { authOptions } from '@/lib/authOptions';

export const POST = createFeedbackHandler({
  transport: nextAuthGmailTransport({ authOptions }),
  getUser: nextAuthGetUser({ authOptions }),
  recipientEmail: 'you@example.com',
});

Props

Prop Type Default Description
user { name?, email? } | null null Submitter identity. If omitted, the composer shows an optional "Your email" field.
appName string env or "App" Prefix in the email subject.
endpoint string "/api/feedback" POST target.
accentColor string "#111827" Hex color for pin, Send button, screenshot marker.
enabled boolean true Force-disable. Also respects NEXT_PUBLIC_FEEDBACK_ENABLED=false.

Server handler options

createFeedbackHandler({
  transport,                          // required
  recipientEmail?: string,            // or FEEDBACK_RECIPIENT_EMAIL env
  appName?: string,                   // or FEEDBACK_APP_NAME env
  getUser?: (request) => Promise<{ name?, email? } | null>,
})

If getUser returns a value, it overrides whatever the client sent (useful when you trust only server-side auth).

Env vars (all optional)

Variable Side Purpose
NEXT_PUBLIC_FEEDBACK_ENABLED client "false" disables the widget without removing the mount.
NEXT_PUBLIC_FEEDBACK_APP_NAME client Fallback for the appName prop.
FEEDBACK_RECIPIENT_EMAIL server Fallback for the recipientEmail option.
FEEDBACK_APP_NAME server Fallback for the appName option.

How it works

  • Press c (or click the floating hint) → placing mode. Cursor goes crosshair.
  • Click anywhere → pin drops at that spot. A composer popover anchors next to it, showing what DOM element you pointed at.
  • "skip pin" button in the banner → submit general, unanchored feedback instead.
  • Send → renders a full-page screenshot via html2canvas-pro, draws the pin on top, and POSTs multipart/form-data to the endpoint.
  • Server handler → builds a subject/body/HTML email and hands it to the configured transport.

Only pinned feedback captures a screenshot; general feedback skips it to save latency.

License

MIT

About

A simple tool for capturing user feedback in live prototypes

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors