React Integration
Comprehensive guide to integrating FeedbackKit with React applications using hooks, context providers, and optimized component patterns.
React Hooks
Custom hooks for seamless feedback management
Feedback Widget
Pre-built and customizable feedback overlay
State Management
Context providers and state synchronization
Data Fetching
Optimized queries with caching and SWR
Configuration
Flexible configuration and theming
Performance
Optimized for React 18 and concurrent features
Installation & Setup
Install FeedbackKit Widget
Add FeedbackKit to your React project
# Install the widget package
npm install @feedbackkit/widget
# Install peer dependencies
npm install @feedbackkit/js react react-dom
# For TypeScript projects
npm install -D @types/react @types/react-domWidget Configuration
Basic Widget Configuration
Essential props for getting started
import { FeedbackWidget } from '@feedbackkit/widget';
export default function MyApp() {
return (
<div>
<h1>My Website</h1>
<FeedbackWidget
apiKey="feedbackkit_sk_your_api_key_here"
appName="My App"
trigger="floating"
position="bottom-right"
onSuccess={() => {
console.log('Feedback submitted!');
}}
/>
</div>
);
}Hooks & State Management
Feedback Hooks
Manage feedback state and operations
import { useState, useCallback } from 'react';
// Custom hook for feedback submission
export function useFeedbackSubmission() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const submitFeedback = useCallback(async (feedbackData: {
type: 'bug' | 'feature' | 'error' | 'general';
priority: 'low' | 'medium' | 'high' | 'critical';
title: string;
description: string;
email?: string;
screenshot?: string;
}) => {
setIsSubmitting(true);
setError(null);
try {
const response = await fetch('https://api.feedbackkit.io/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.REACT_APP_FEEDBACKKIT_API_KEY,
},
body: JSON.stringify({
...feedbackData,
metadata: {
url: window.location.href,
userAgent: navigator.userAgent,
appName: 'My App',
timestamp: new Date().toISOString(),
},
}),
});
if (!response.ok) {
throw new Error('Failed to submit feedback');
}
const result = await response.json();
return result;
} catch (err: any) {
setError(err.message);
throw err;
} finally {
setIsSubmitting(false);
}
};
return {
submitFeedback,
isSubmitting,
error,
};
}
// Usage example
function FeedbackForm() {
const { submitFeedback, isSubmitting, error } = useFeedbackSubmission();
const [formData, setFormData] = useState({
type: 'bug' as const,
priority: 'medium' as const,
title: '',
description: '',
email: '',
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await submitFeedback(formData);
// Show success message
alert('Feedback submitted successfully!');
} catch (error) {
// Error is already handled by the hook
console.error('Failed to submit feedback:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<select
value={formData.type}
onChange={(e) => setFormData(prev => ({ ...prev, type: e.target.value as any }))}
>
<option value="bug">Bug Report</option>
<option value="feature">Feature Request</option>
<option value="error">Error Report</option>
<option value="general">General Feedback</option>
</select>
<select
value={formData.priority}
onChange={(e) => setFormData(prev => ({ ...prev, priority: e.target.value as any }))}
>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
<input
type="text"
value={formData.title}
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
placeholder="Brief description"
required
/>
<textarea
value={formData.description}
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
placeholder="Detailed information..."
required
/>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
placeholder="Your email (optional)"
/>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit Feedback'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}Performance Optimization
React 18 Features
// Lazy load the feedback widget
import { lazy, Suspense } from 'react';
const FeedbackWidget = lazy(() => import('@feedbackkit/widget').then(module => ({
default: module.FeedbackWidget
})));
function App() {
return (
<div>
<h1>My Website</h1>
<Suspense fallback={<div>Loading feedback widget...</div>}>
<FeedbackWidget
apiKey="your-api-key"
appName="My App"
/>
</Suspense>
</div>
);
}
// Use startTransition for non-urgent updates
import { startTransition } from 'react';
function FeedbackForm() {
const [searchTerm, setSearchTerm] = useState('');
const [deferredSearchTerm, setDeferredSearchTerm] = useState('');
const handleSearch = (value: string) => {
setSearchTerm(value);
// Mark as non-urgent update
startTransition(() => {
setDeferredSearchTerm(value);
});
};
return (
<div>
<input
value={searchTerm}
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search feedback..."
/>
<FeedbackList searchTerm={deferredSearchTerm} />
</div>
);
}Caching Strategies
// Memoize expensive calculations
import { useMemo, useCallback } from 'react';
function FeedbackStats({ feedbacks }: { feedbacks: any[] }) {
const stats = useMemo(() => {
return {
total: feedbacks.length,
byType: feedbacks.reduce((acc, f) => {
acc[f.type] = (acc[f.type] || 0) + 1;
return acc;
}, {}),
byPriority: feedbacks.reduce((acc, f) => {
acc[f.priority] = (acc[f.priority] || 0) + 1;
return acc;
}, {}),
averagePriority: feedbacks.reduce((sum, f) => {
const priorityValues = { low: 1, medium: 2, high: 3, critical: 4 };
return sum + priorityValues[f.priority];
}, 0) / feedbacks.length,
};
}, [feedbacks]);
return <StatsDisplay stats={stats} />;
}
// Optimize re-renders with useCallback
function FeedbackWidget({ onSuccess }: { onSuccess: () => void }) {
const handleSuccess = useCallback(() => {
onSuccess();
// Additional success handling
}, [onSuccess]);
return (
<FeedbackWidget
apiKey="your-api-key"
appName="My App"
onSuccess={handleSuccess}
/>
);
}
// Use React.memo for expensive components
const FeedbackList = React.memo(({ feedbacks }: { feedbacks: any[] }) => {
return (
<div>
{feedbacks.map(feedback => (
<FeedbackCard key={feedback.id} feedback={feedback} />
))}
</div>
);
});Best Practices
Component Design
Single Responsibility
Keep components focused on one responsibility. Separate data fetching, business logic, and presentation.
Error Boundaries
Wrap FeedbackKit components in error boundaries to prevent entire app crashes.
Accessibility
Ensure the feedback widget is accessible with proper ARIA labels and keyboard navigation.
State Management
Local State First
Keep feedback state as local as possible. Only lift state up when multiple components need it.
Memoization
Use React.memo, useMemo, and useCallback appropriately to prevent unnecessary re-renders.
Loading States
Always provide loading states for feedback submission to improve user experience.