Developer guide

How to Collect Feedback in React & Next.js

Add feedback collection to a React or Next.js app with a drop-in widget or a direct API call โ€” including the App Router and CSP details that trip people up.

The Pollenate Team7 min read

You can collect feedback in a React or Next.js app two ways: drop in the Pollenate script tag for a zero-config widget, or call the collect API directly from your own components for full control over the UI. This guide covers both, including the Next.js App Router specifics that trip people up.

Option 1: The drop-in widget (fastest)

For most apps, the script-tag widget is the quickest path. In a standard React app, add it once near your root so it persists across client-side navigation.

In Next.js (App Router), use the next/script component in your root layout so it loads after hydration:

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://pollenate.dev/widget.js"
          data-inbox-key={process.env.NEXT_PUBLIC_POLLENATE_INBOX_KEY}
          data-api-key={process.env.NEXT_PUBLIC_POLLENATE_API_KEY}
          data-type="emoji"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

In Vite or Create React App, add the same script to index.html, or inject it in a top-level useEffect if you prefer to keep it in code.

Option 2: Call the collect API directly

When you want feedback collection to live inside your own design system, skip the widget and POST to the collect endpoint from a component. This gives you complete control over the markup and interaction.

import { useState } from 'react';

const API = 'https://api.pollenate.dev';

export function FeedbackButtons({ page }: { page: string }) {
  const [sent, setSent] = useState(false);

  async function submit(score: number) {
    await fetch(`${API}/collect`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Pollenate-Key': import.meta.env.VITE_POLLENATE_API_KEY,
      },
      body: JSON.stringify({
        inboxKey: import.meta.env.VITE_POLLENATE_INBOX_KEY,
        type: 'thumbs',
        score,
        meta: { page },
      }),
    });
    setSent(true);
  }

  if (sent) return <p>Thanks for the feedback!</p>;

  return (
    <div>
      <span>Was this helpful?</span>
      <button onClick={() => submit(1)} aria-label="Yes">๐Ÿ‘</button>
      <button onClick={() => submit(0)} aria-label="No">๐Ÿ‘Ž</button>
    </div>
  );
}

For typed projects, the @pollenate/react package ships a fully typed drop-in component and hooks so you don't have to hand-roll the fetch call.

Where should you put your API keys?

Use a collect-scoped API key for anything that runs in the browser. The collect scope can only submit feedback โ€” it can't read data or change settings โ€” so it's safe to expose in client-side code, the same way analytics keys are. Keep admin-scoped keys server-side only.

In Next.js, prefix browser-exposed values with NEXT_PUBLIC_. In Vite, prefix them with VITE_.

Handling client-side navigation

Single-page apps don't trigger full page loads, so make sure feedback context updates as the route changes. If you call the API directly, pass the current path in meta on each submission (as shown above). If you use the widget, it reads window.location at submit time, so it already captures the active route.

Content Security Policy

If your app sets a strict CSP, allow Pollenate's origins so the widget and API calls aren't blocked. Add to your policy:

  • script-src https://pollenate.dev
  • connect-src https://api.pollenate.dev https://pollenate.dev

A blocked widget usually shows a "Failed to fetch" error in the console โ€” that's almost always a missing CSP entry.

Next steps

Once feedback is flowing, set up an automation so new responses post to Slack or open a GitHub issue, and use AI semantic search to find recurring themes across everything your users tell you.

Frequently Asked Questions

How do I add a feedback widget in Next.js?
Use the next/script component in your root layout with strategy="afterInteractive" and your inbox and API keys as data attributes.
Is it safe to put my Pollenate API key in client-side code?
Yes, if it is a collect-scoped key. The collect scope can only submit feedback, not read data or change settings, so it is safe to expose like an analytics key.
Why does my widget show a "Failed to fetch" error?
That almost always means your Content Security Policy is blocking Pollenate. Add pollenate.dev to script-src and api.pollenate.dev to connect-src.

Start collecting feedback in minutes

Add Pollenate to any site with one line of code. Free to start โ€” no credit card required.