React Component

Use the Feedhog widget as a React component in React, Next.js, and other React-based frameworks.

React Component

The @feedhog/widget package provides a React component for React and Next.js applications.

Installation

npm install @feedhog/widget
yarn add @feedhog/widget
pnpm add @feedhog/widget

Basic Usage

import { FeedhogWidget } from '@feedhog/widget/react';

function App() {
  return (
    <>
      {/* Your app content */}
      <FeedhogWidget apiKey="fhpk_your_public_key" />
    </>
  );
}

Props

interface FeedhogWidgetProps {
  /** Public API key (required) */
  apiKey: string;

  /** Base URL for the API */
  baseUrl?: string;

  /** Widget position on the page */
  position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";

  /** Primary brand color (hex) */
  primaryColor?: string;

  /** Trigger button text */
  triggerText?: string;

  /** Show/hide the trigger button */
  showTrigger?: boolean;

  /** Modal title */
  title?: string;

  /** Modal subtitle */
  subtitle?: string;

  /** User identity */
  user?: {
    externalId: string;
    email?: string;
    name?: string;
    avatarUrl?: string;
    metadata?: Record<string, unknown>;
  };
}

Full Example

import { FeedhogWidget } from '@feedhog/widget/react';

function App() {
  const currentUser = useAuth(); // Your auth hook

  return (
    <>
      <Header />
      <Main />
      <Footer />

      <FeedhogWidget
        apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
        position="bottom-right"
        primaryColor="#8b5cf6"
        triggerText="Share Ideas"
        title="What would you like to see?"
        subtitle="Help us build better products"
        user={currentUser ? {
          externalId: currentUser.id,
          email: currentUser.email,
          name: currentUser.name,
          avatarUrl: currentUser.avatarUrl,
          metadata: {
            plan: currentUser.plan
          }
        } : undefined}
      />
    </>
  );
}

Next.js App Router

Create a client component for the widget:

// components/feedback-widget.tsx
'use client';

import { FeedhogWidget } from '@feedhog/widget/react';
import { useAuth } from '@/hooks/use-auth';

export function FeedbackWidget() {
  const { user, isAuthenticated } = useAuth();

  return (
    <FeedhogWidget
      apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
      position="bottom-right"
      user={isAuthenticated && user ? {
        externalId: user.id,
        email: user.email,
        name: user.name,
        avatarUrl: user.image
      } : undefined}
    />
  );
}

Add to your root layout:

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <FeedbackWidget />
      </body>
    </html>
  );
}

Next.js Pages Router

// pages/_app.tsx
import { FeedhogWidget } from '@feedhog/widget/react';
import { useSession } from 'next-auth/react';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  const { data: session } = useSession();

  return (
    <>
      <Component {...pageProps} />
      <FeedhogWidget
        apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
        user={session?.user ? {
          externalId: session.user.id,
          email: session.user.email!,
          name: session.user.name!
        } : undefined}
      />
    </>
  );
}

Custom Trigger

Hide the default trigger and use your own button:

'use client';

import { FeedhogWidget } from '@feedhog/widget/react';
import { Button } from '@/components/ui/button';
import { MessageSquare } from 'lucide-react';

export function FeedbackButton() {
  return (
    <>
      <FeedhogWidget
        apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
        showTrigger={false}
      />

      <Button
        onClick={() => window.FeedhogWidget?.open()}
        variant="outline"
        size="sm"
      >
        <MessageSquare className="mr-2 h-4 w-4" />
        Feedback
      </Button>
    </>
  );
}

Conditional Rendering

Only show the widget on certain pages:

// app/layout.tsx
'use client';

import { usePathname } from 'next/navigation';
import { FeedhogWidget } from '@feedhog/widget/react';

export function FeedbackProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();

  // Don't show on landing pages
  const showWidget = pathname.startsWith('/dashboard') ||
                     pathname.startsWith('/app');

  return (
    <>
      {children}
      {showWidget && (
        <FeedhogWidget apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!} />
      )}
    </>
  );
}

Dynamic User Updates

The widget automatically re-identifies when the user prop changes:

'use client';

import { useEffect, useState } from 'react';
import { FeedhogWidget } from '@feedhog/widget/react';

export function DynamicWidget() {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    // Fetch user on mount
    fetchCurrentUser().then(setUser);
  }, []);

  // Widget updates when user changes
  return (
    <FeedhogWidget
      apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
      user={user ? {
        externalId: user.id,
        email: user.email,
        name: user.name
      } : undefined}
    />
  );
}

Using with Auth Providers

NextAuth.js

'use client';

import { FeedhogWidget } from '@feedhog/widget/react';
import { useSession } from 'next-auth/react';

export function FeedbackWidget() {
  const { data: session, status } = useSession();

  // Don't render until we know auth state
  if (status === 'loading') return null;

  return (
    <FeedhogWidget
      apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
      user={session?.user ? {
        externalId: session.user.id,
        email: session.user.email!,
        name: session.user.name!,
        avatarUrl: session.user.image || undefined
      } : undefined}
    />
  );
}

Clerk

'use client';

import { FeedhogWidget } from '@feedhog/widget/react';
import { useUser } from '@clerk/nextjs';

export function FeedbackWidget() {
  const { user, isLoaded, isSignedIn } = useUser();

  if (!isLoaded) return null;

  return (
    <FeedhogWidget
      apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
      user={isSignedIn && user ? {
        externalId: user.id,
        email: user.primaryEmailAddress?.emailAddress,
        name: user.fullName || undefined,
        avatarUrl: user.imageUrl
      } : undefined}
    />
  );
}

Supabase Auth

'use client';

import { FeedhogWidget } from '@feedhog/widget/react';
import { useUser } from '@supabase/auth-helpers-react';

export function FeedbackWidget() {
  const user = useUser();

  return (
    <FeedhogWidget
      apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!}
      user={user ? {
        externalId: user.id,
        email: user.email,
        name: user.user_metadata?.full_name
      } : undefined}
    />
  );
}

TypeScript Types

Import types for type safety:

import { FeedhogWidget } from '@feedhog/widget/react';
import type { FeedhogWidgetProps, UserIdentity } from '@feedhog/widget/react';

// Use types
const user: UserIdentity = {
  externalId: 'user-123',
  email: 'user@example.com'
};

const props: FeedhogWidgetProps = {
  apiKey: 'fhpk_your_public_key',
  user
};

Widget API

Access the widget API through window.FeedhogWidget:

'use client';

import { useEffect } from 'react';
import { FeedhogWidget } from '@feedhog/widget/react';

export function WidgetController() {
  useEffect(() => {
    // Widget methods are available after mount
    const openWidget = () => window.FeedhogWidget?.open();
    const closeWidget = () => window.FeedhogWidget?.close();

    // Example: Open widget when URL has ?feedback=true
    const params = new URLSearchParams(window.location.search);
    if (params.get('feedback') === 'true') {
      // Small delay to ensure widget is mounted
      setTimeout(openWidget, 100);
    }
  }, []);

  return <FeedhogWidget apiKey={process.env.NEXT_PUBLIC_FEEDHOG_API_KEY!} />;
}