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/widgetyarn add @feedhog/widgetpnpm add @feedhog/widgetBasic 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!} />;
}