Next.js Integration
Server-side helpers for Next.js App Router
npm install @fdback.io/sdk server-onlyThe /next subpath includes server-only protection to prevent accidental client-side imports.
Option 1: API Route Handler
Create a route that handles signing:
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";
export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
});With Authentication
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";
export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
authorize: async () => {
const session = await auth();
return !!session?.user;
},
getUser: async () => {
const session = await auth();
if (!session?.user?.email) return null;
return {
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
};
},
});Option 2: Server Action
Create a server action for use with React Server Components:
// lib/fdback.ts
"use server";
import { createSignAction } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";
const _signAction = createSignAction({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
});
// Wrap with auth check
export async function signFdbackLogin() {
const session = await auth();
if (!session?.user?.email) {
return { success: false as const, error: "Unauthorized" };
}
return _signAction({
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
});
}Use in client component:
"use client";
import { signFdbackLogin } from "@/lib/fdback";
import { useFdback } from "@fdback.io/sdk/react";
export function FeedbackButton() {
const { open } = useFdback();
const handleClick = async () => {
const result = await signFdbackLogin();
if (result.success) {
open(result.data);
} else {
console.error(result.error);
}
};
return <button onClick={handleClick}>Feedback</button>;
}API Reference
| Export | Description |
|---|---|
createSignHandler(options) | Creates a POST route handler |
createSignAction(options) | Creates a server action factory |
SignHandlerOptions
interface SignHandlerOptions {
workspaceId: string;
workspaceSecret: string;
baseUrl?: string;
authorize?: (request: Request) => Promise<boolean> | boolean;
getUser?: (request: Request) => Promise<FdbackUser | null> | FdbackUser | null;
}SignActionOptions
interface SignActionOptions {
workspaceId: string;
workspaceSecret: string;
baseUrl?: string;
}Complete Example
1. Environment variables
FDBACK_WORKSPACE_ID=your-workspace-id
FDBACK_WORKSPACE_SECRET=your-workspace-secret
NEXT_PUBLIC_FDBACK_WORKSPACE_ID=your-workspace-id2. API route
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";
export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
authorize: async () => !!(await auth())?.user,
getUser: async () => {
const session = await auth();
if (!session?.user?.email) return null;
return {
email: session.user.email,
name: session.user.name ?? undefined,
};
},
});3. Provider setup
// app/layout.tsx
import { FdbackProvider } from "@fdback.io/sdk/react";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<FdbackProvider
workspaceId={process.env.NEXT_PUBLIC_FDBACK_WORKSPACE_ID!}
signEndpoint="/api/fdback/sign"
>
{children}
</FdbackProvider>
</body>
</html>
);
}4. Feedback button
// components/feedback-button.tsx
"use client";
import { FeedbackButton } from "@fdback.io/sdk/react";
import { useSession } from "next-auth/react";
export function AppFeedbackButton() {
const { data: session } = useSession();
if (!session?.user?.email) return null;
return (
<FeedbackButton
user={{
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
}}
className="fixed bottom-4 right-4 rounded-full bg-primary px-4 py-2 text-white"
>
Feedback
</FeedbackButton>
);
}