Skip to main content

Install on React / Next.js

For React-based apps, the cleanest install is the @pay4feedback/react npm package. It wraps the widget IIFE in a context provider, exposes a typed hook, and ships an opinionated trigger button.

Install

npm install @pay4feedback/react
# or: pnpm add @pay4feedback/react / yarn add @pay4feedback/react

Quick start (Vite, CRA, Remix, etc.)

import { Pay4FeedbackProvider, FeedbackButton } from '@pay4feedback/react';

export default function App() {
return (
<Pay4FeedbackProvider
tenantId={import.meta.env.VITE_P4F_TENANT_ID}
apiKey={import.meta.env.VITE_P4F_WIDGET_KEY}
campaignId={import.meta.env.VITE_P4F_CAMPAIGN_ID}
trigger={{ type: 'time', value: 10 }}
>
<YourApp />
<FeedbackButton className="btn">How are we doing?</FeedbackButton>
</Pay4FeedbackProvider>
);
}

That's the whole integration. The provider lazy-loads the widget bundle on mount; the button renders nothing until the SDK is ready, so users never see a dead button.

Next.js (App Router)

The widget is browser-only, so the provider goes inside a Client Component. Wrap once at the layout root:

// app/providers.tsx
"use client";
import { Pay4FeedbackProvider } from '@pay4feedback/react';

export default function Providers({ children }: { children: React.ReactNode }) {
return (
<Pay4FeedbackProvider
tenantId={process.env.NEXT_PUBLIC_P4F_TENANT_ID!}
apiKey={process.env.NEXT_PUBLIC_P4F_WIDGET_KEY!}
campaignId={process.env.NEXT_PUBLIC_P4F_CAMPAIGN_ID!}
>
{children}
</Pay4FeedbackProvider>
);
}
// app/layout.tsx
import Providers from './providers';

export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}

The widget API key is a widget key (different from the public REST API key) and is safe to expose in client-side code. NEXT_PUBLIC_* is the right env-var prefix.

Triggering manually

Set trigger={{ type: 'manual' }} and call show() from anywhere in the tree:

import { usePay4Feedback } from '@pay4feedback/react';

function ExitButton() {
const { show } = usePay4Feedback();
return <button onClick={show}>Share thoughts and earn $2</button>;
}

Use this for "open after the user finishes the onboarding tour", "open from the help menu", etc.

Identifying logged-in users

When you have the user's identity, pass it to identify() so feedback responses can be correlated with your user base server-side.

import { useEffect } from 'react';
import { usePay4Feedback } from '@pay4feedback/react';
import { useUser } from '@/hooks/use-user';

function Identity() {
const { identify } = usePay4Feedback();
const user = useUser();

useEffect(() => {
if (!user) return;
identify({
userId: user.id,
// Email transmitted only if you have legitimate basis under GDPR.
email: user.email,
attributes: { plan: user.plan, locale: user.locale },
});
}, [user, identify]);

return null;
}

identify() is safe to call before the SDK loads — the package buffers the call and replays it once the widget initialises. So you can call it directly inside useEffect without worrying about ready state.

API surface

<Pay4FeedbackProvider />

PropTypeRequiredDefault
tenantIdstringyes
apiKeystringyes
campaignIdstringyes
trigger{ type, value? }nonone (server default)
baseUrlstringnohttps://app.pay4feedback.com
scriptSrcstringnoderived from baseUrl
loadEvenWithoutKeybooleannofalse

trigger.type is 'time' | 'scroll' | 'exit_intent' | 'manual'. value is seconds for time, percent for scroll.

usePay4Feedback()

{
isReady: boolean;
show: () => void;
hide: () => void;
identify: (user: {
userId?: string;
email?: string;
attributes?: Record<string, string | number | boolean>;
}) => void;
}

<FeedbackButton />

Pre-wired button that renders nothing until isReady is true. Accepts every standard <button> prop. If you pass onClick, it runs first and can event.preventDefault() to skip opening the widget.

Vue, Angular, Svelte

The widget IIFE works fine in any framework — the React package is a convenience layer. For non-React stacks, follow the JavaScript API install option. Dedicated packages for Vue and Svelte are on the roadmap.