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
npm install @feedvalue/react
# or
pnpm add @feedvalue/react
# or
yarn add @feedvalue/reactApp Router (Next.js 13+)
The FeedValue React package is a client component. Wrap your app with the provider in your root layout:
// 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:
// 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:
// 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:
// 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>
);
}// 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:
// 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:
// 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.local
NEXT_PUBLIC_FEEDVALUE_WIDGET_ID=your-widget-id<FeedValueProvider
widgetId={process.env.NEXT_PUBLIC_FEEDVALUE_WIDGET_ID!}
>Event Callbacks
Track feedback events with analytics:
// 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):
// 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:
// 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
| Feature | Default Mode | Headless Mode |
|---|---|---|
| Trigger button | Dashboard-styled | You build it |
| Modal | Dashboard-styled | You build it |
| SSR compatibility | Yes | Yes |
| API methods | Available | Available |
| User tracking | Available | Available |
TIP
Use headless={true} when you want the feedback UI to match your app's design system exactly.
