Integrations

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-dom

Widget 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.