Next.js App Router
Render thefaqapp content in a Next.js 15+ app with SSR, ISR, and useFaq hooks.
Updated 2026-05-19
The most common integration: a Next.js app rendering FAQ content on a public marketing or help surface.
bun add @faqapp/core @faqapp/react
Server-side render a list
// app/help/page.tsx
import { createFAQClient } from "@faqapp/core";
export const revalidate = 300; // ISR — refresh every 5 minutes
export default async function HelpPage() {
const faq = createFAQClient({
apiKey: process.env.FAQAPP_API_KEY!,
organizationSlug: "acme"
});
const { data: questions } = await faq.questions.list({ limit: 50 });
return (
<main>
<h1>Help</h1>
{questions.map((q) => (
<article key={q.id}>
<h2>{q.title}</h2>
<p>{q.answer}</p>
</article>
))}
</main>
);
}
The SDK is dependency-free, so it ships fine to RSC. FAQAPP_API_KEY should be a read-scope key, server-side only.
Per-question dynamic route
// app/help/[slug]/page.tsx
import { createFAQClient } from "@faqapp/core";
import { notFound } from "next/navigation";
export async function generateStaticParams() {
const faq = createFAQClient({
apiKey: process.env.FAQAPP_API_KEY!,
organizationSlug: "acme"
});
const { data } = await faq.questions.list({ limit: 100 });
return data.map((q) => ({ slug: q.slug }));
}
export default async function QuestionPage({ params }) {
const faq = createFAQClient({
apiKey: process.env.FAQAPP_API_KEY!,
organizationSlug: "acme"
});
try {
const { data: q } = await faq.questions.get(params.slug);
return (
<article>
<h1>{q.title}</h1>
<p>{q.answer}</p>
</article>
);
} catch (err) {
if (err.code === "question_not_found") notFound();
throw err;
}
}
Client-side hooks (interactive surfaces)
For client-side search, expanding rows, AI ask, or user-specific filtering:
// app/help/SearchableList.tsx
"use client";
import { FAQClientProvider, useFaq } from "@faqapp/react";
export function SearchableList() {
return (
<FAQClientProvider
organizationSlug="acme"
apiKey={process.env.NEXT_PUBLIC_FAQAPP_READ_KEY!}
>
<Inner />
</FAQClientProvider>
);
}
function Inner() {
const [q, setQ] = useState("");
const { data } = useFaq().questions.list({ q, limit: 20 });
return (
<div>
<input value={q} onChange={(e) => setQ(e.target.value)} />
{data?.map((x) => <Row key={x.id} {...x} />)}
</div>
);
}
For the public client component, use a read-scope API key exposed as NEXT_PUBLIC_*. Don’t expose write or admin keys to the browser.
Revalidate on publish
Wire a webhook from thefaqapp to a Next.js Route Handler that calls revalidatePath:
// app/api/faqapp-webhook/route.ts
import { revalidatePath } from "next/cache";
export async function POST(req: Request) {
const sig = req.headers.get("X-FAQApp-Signature");
// Verify signature against your webhook secret
// ...
const event = await req.json();
if (event.type === "question.published") {
revalidatePath("/help");
revalidatePath(`/help/${event.question.slug}`);
}
return new Response("ok");
}
Add the webhook URL in your dashboard under Settings → Webhooks. The signing secret lives in your env as FAQAPP_WEBHOOK_SECRET.