Skip to content

Next.js Integration

Add FeedValue to your Next.js application with App Router or Pages Router.

Coming Soon

The @feedvalue/react package is currently in development. Check back soon for availability on npm. In the meantime, use the script tag method with Next.js Script component.

Installation

bash
npm install @feedvalue/react
# or
pnpm add @feedvalue/react
# or
yarn add @feedvalue/react

App Router (Next.js 13+)

The FeedValue React package is a client component. Wrap your app with the provider in your root layout:

tsx
// app/layout.tsx
import { FeedValueProvider } from '@feedvalue/react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <FeedValueProvider widgetId="your-widget-id">
          {children}
        </FeedValueProvider>
      </body>
    </html>
  );
}

Then use the hook in any client component:

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

import { useFeedValue } from '@feedvalue/react';

export function FeedbackButton() {
  const { open, isReady } = useFeedValue();

  return (
    <button onClick={open} disabled={!isReady}>
      Give Feedback
    </button>
  );
}

Pages Router

Add to _app.tsx:

tsx
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { FeedValueProvider } from '@feedvalue/react';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <FeedValueProvider widgetId="your-widget-id">
      <Component {...pageProps} />
    </FeedValueProvider>
  );
}

Headless Mode (Custom UI)

For complete control over the feedback UI:

tsx
// app/layout.tsx
import { FeedValueProvider } from '@feedvalue/react';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <FeedValueProvider widgetId="your-widget-id" headless>
          {children}
        </FeedValueProvider>
      </body>
    </html>
  );
}
tsx
// components/custom-feedback.tsx
'use client';

import { useState } from 'react';
import { useFeedValue } from '@feedvalue/react';

export function CustomFeedback() {
  const { isReady, isOpen, open, close, submit, isSubmitting } = useFeedValue();
  const [message, setMessage] = useState('');

  const handleSubmit = async () => {
    await submit({ message });
    setMessage('');
    close();
  };

  if (!isReady) return null;

  return (
    <>
      <button onClick={open}>Feedback</button>

      {isOpen && (
        <dialog open className="feedback-modal">
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            placeholder="Your feedback..."
          />
          <div>
            <button onClick={handleSubmit} disabled={isSubmitting}>
              Submit
            </button>
            <button onClick={close}>Cancel</button>
          </div>
        </dialog>
      )}
    </>
  );
}

User Identification

Track logged-in users:

tsx
// components/user-tracker.tsx
'use client';

import { useEffect } from 'react';
import { useFeedValue } from '@feedvalue/react';
import { useUser } from '@/hooks/use-user'; // Your auth hook

export function UserTracker() {
  const { identify, reset } = useFeedValue();
  const user = useUser();

  useEffect(() => {
    if (user) {
      identify(user.id, {
        email: user.email,
        name: user.name,
        plan: user.subscription?.plan,
      });
    } else {
      reset();
    }
  }, [user, identify, reset]);

  return null;
}

Add to your layout:

tsx
// app/layout.tsx
import { FeedValueProvider } from '@feedvalue/react';
import { UserTracker } from '@/components/user-tracker';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <FeedValueProvider widgetId="your-widget-id">
          <UserTracker />
          {children}
        </FeedValueProvider>
      </body>
    </html>
  );
}

Environment Variables

Store your widget ID in environment variables:

env
# .env.local
NEXT_PUBLIC_FEEDVALUE_WIDGET_ID=your-widget-id
tsx
<FeedValueProvider
  widgetId={process.env.NEXT_PUBLIC_FEEDVALUE_WIDGET_ID!}
>

Event Callbacks

Track feedback events with analytics:

tsx
// app/layout.tsx
import { FeedValueProvider } from '@feedvalue/react';
import { analytics } from '@/lib/analytics';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <FeedValueProvider
          widgetId="your-widget-id"
          onOpen={() => analytics.track('feedback_opened')}
          onSubmit={(feedback) => {
            analytics.track('feedback_submitted', {
              sentiment: feedback.sentiment,
              hasMessage: !!feedback.message,
            });
          }}
          onError={(error) => {
            console.error('FeedValue error:', error);
          }}
        >
          {children}
        </FeedValueProvider>
      </body>
    </html>
  );
}

Script Tag Alternative

If you prefer the script tag approach (without React integration):

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

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://cdn.feedvalue.com/widget.js"
          data-widget-id="your-widget-id"
          strategy="lazyOnload"
        />
      </body>
    </html>
  );
}

WARNING

The script tag approach doesn't provide React hooks or programmatic control. Use @feedvalue/react for full integration.

Server Components Compatibility

The FeedValueProvider is a client component (uses 'use client'). It works seamlessly with Server Components:

tsx
// app/page.tsx (Server Component)
import { FeedbackButton } from '@/components/feedback-button';

export default async function Page() {
  const data = await fetchData(); // Server-side fetch

  return (
    <div>
      <h1>Dashboard</h1>
      {/* Client component works within server component */}
      <FeedbackButton />
    </div>
  );
}

Comparison: Default vs Headless

FeatureDefault ModeHeadless Mode
Trigger buttonDashboard-styledYou build it
ModalDashboard-styledYou build it
SSR compatibilityYesYes
API methodsAvailableAvailable
User trackingAvailableAvailable

TIP

Use headless={true} when you want the feedback UI to match your app's design system exactly.

Built with care by SarverEnterprises