This guide walks through a production-ready Feedtack setup with a Next.js API route, Zod validation, and rate limiting.
1. Install Feedtack
npm install feedtack2. Create the API route
// app/api/feedtack/route.ts
import { NextResponse } from "next/server";
import { z } from "zod";
const PayloadSchema = z.object({
schemaVersion: z.string(),
id: z.string(),
timestamp: z.string(),
scope: z.enum(["site", "page", "element"]),
submittedBy: z.object({
id: z.string(),
name: z.string(),
role: z.string(),
}),
comment: z.string().min(1).max(5000),
sentiment: z.enum(["good", "bad"]).nullable(),
pins: z.array(z.any()),
page: z.object({
url: z.string(),
pathname: z.string(),
title: z.string(),
}),
viewport: z.any(),
device: z.any(),
});
export async function POST(request: Request) {
const body = await request.json();
const result = PayloadSchema.safeParse(body);
if (!result.success) {
return NextResponse.json({ error: "Invalid payload" }, { status: 400 });
}
// Store the feedback — database, file, etc.
// await db.insert("feedtack_submissions", result.data);
return NextResponse.json({ ok: true });
}
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const pathname = searchParams.get("pathname");
// Load feedback from your database
// const items = await db.query("feedtack_submissions", { pathname });
return NextResponse.json([]);
}3. Wire up the provider
// app/layout.tsx or providers/FeedtackProvider.tsx
"use client";
import { FeedtackProvider } from "feedtack/react";
import { WebhookAdapter } from "feedtack";
const adapter = new WebhookAdapter({
submitUrl: "/api/feedtack",
loadFeedback: async (filter) => {
const res = await fetch(`/api/feedtack?pathname=${filter?.pathname ?? ""}`);
return res.json();
},
});
export function FeedtackWrapper({ children, user }: { children: React.ReactNode; user: any }) {
return (
<FeedtackProvider
adapter={adapter}
currentUser={user}
adminOnly
onError={console.error}
>
{children}
</FeedtackProvider>
);
}4. Gate visibility
Use adminOnly to restrict the feedback button to admin users. Non-admin users won't see the button, but they can still use the useFeedtack hook if you build custom UI for them.
For more granular control, conditionally render the provider:
{user.role === "admin" || user.role === "designer" ? (
<FeedtackProvider adapter={adapter} currentUser={user}>
{children}
</FeedtackProvider>
) : (
children
)}5. Error handling
Always pass onError in production to catch adapter failures:
<FeedtackProvider
adapter={adapter}
currentUser={user}
onError={(err) => {
// Send to your error tracking service
Sentry.captureException(err);
}}
>