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 />
| Prop | Type | Required | Default |
|---|---|---|---|
tenantId | string | yes | — |
apiKey | string | yes | — |
campaignId | string | yes | — |
trigger | { type, value? } | no | none (server default) |
baseUrl | string | no | https://app.pay4feedback.com |
scriptSrc | string | no | derived from baseUrl |
loadEvenWithoutKey | boolean | no | false |
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.