@loudevuno/feedback-widget
v1.1.0
Published
Reusable feedback widget component with Linear integration
Maintainers
Readme
Smart Feedback Widget
A reusable, dark-themed feedback component for React applications. Modeled after the Linear/Raycast command palette aesthetic, it allows users to submit bug reports, feature requests, and general feedback with file attachments.
Package Update Workflow
This package is published to npm as @loudevuno/feedback-widget. When updates are made to this repository, here's how they propagate to downstream applications:
1. Development & Build
# Make changes to the widget in this repository
npm run build # Build the package for distribution2. Publish to npm
# Update version in package.json (following semver)
npm version patch # or minor/major depending on changes
npm publish # Publishes to npm registry3. Update Downstream Apps
In each application that uses this widget:
# Update to latest version
npm install @loudevuno/feedback-widget@latest
# Or update package.json manually and run
npm install4. Deploy Apps
After updating the dependency, redeploy your applications to production. No code changes are required in the consuming apps - they automatically receive the updated widget UI and functionality.
Quick Reference
- This repo: Source code and development of the widget package
- Downstream apps: Applications that import and use
@loudevuno/feedback-widget - Update flow: Code changes → Build → Publish to npm → Apps update dependency → Apps redeploy
Features
- Linear/Raycast Aesthetic: Clean, dark-mode UI with blur effects.
- Zero External CSS: Uses Tailwind CSS classes.
- Accessibility: Keyboard friendly (Esc to close), proper focus management.
- File Uploads: Drag and drop support for images, logs, and videos.
- Lightweight: Only depends on
framer-motionfor animations and standard React hooks.
Installation
1. Dependencies
Ensure you have the following packages installed:
npm install framer-motion lucide-react
# If you aren't using Tailwind, you'll need to set it up or adapt the classes.Note: The icons in the provided code use a local Icons.tsx file to avoid heavy icon library dependencies, but you can swap them for lucide-react or heroicons easily.
2. Copy Components
Copy the following files into your project structure:
components/FeedbackWidget.tsxcomponents/ui/Icons.tsx(or use your own icon library)types.tsservices/submissionService.ts
3. Usage
Import and render the component at the root of your application (or anywhere global).
import { FeedbackWidget } from './components/FeedbackWidget';
function App() {
return (
<>
<YourMainAppContent />
<FeedbackWidget />
</>
);
}Backend Integration
The widget uses services/submissionService.ts to handle data transmission. You must implement the API call in this file to connect to your backend.
The Payload
The widget sends data in the following format:
interface FeedbackData {
type: 'issue' | 'idea' | 'other';
description: string;
contact?: string; // Optional email/name
files?: File[];
metadata?: Record<string, any>; // url, userAgent
}Example: Connect to Next.js API Route
1. Update services/submissionService.ts:
export const submitFeedback = async (data: FeedbackData) => {
const formData = new FormData();
formData.append('type', data.type);
formData.append('description', data.description);
if (data.contact) formData.append('contact', data.contact);
// Attach files
if (data.files) {
data.files.forEach((file) => formData.append('files', file));
}
const response = await fetch('/api/feedback', {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Failed to submit');
return await response.json();
};2. Create API Route (e.g. app/api/feedback/route.ts):
You can then forward this data to GitHub Issues or Linear.
GitHub Example:
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
export async function POST(req: Request) {
const formData = await req.formData();
const description = formData.get('description') as string;
const type = formData.get('type') as string;
const contact = formData.get('contact') as string;
const title = `[${type.toUpperCase()}] New Feedback`;
const body = `${description}\n\n**Contact:** ${contact || 'N/A'}`;
await octokit.rest.issues.create({
owner: "your-org",
repo: "your-repo",
title: title,
body: body,
labels: [type === 'issue' ? 'bug' : 'enhancement']
});
return Response.json({ success: true });
}