@faqapp/nextjs
v0.1.1
Published
Next.js SDK for TheFAQApp — React hooks, components, SSR/SSG helpers, and SEO utilities
Maintainers
Readme
@faqapp/nextjs
Next.js components, hooks, and SEO utilities for TheFAQApp.
Install
npm install @faqapp/core @faqapp/nextjs
# or
pnpm add @faqapp/core @faqapp/nextjsComponents
FaqList
Renders a searchable, filterable FAQ list with category grouping:
import { FAQClient } from "@faqapp/core";
import { FaqList } from "@faqapp/nextjs/components";
export default async function FAQPage() {
const faq = new FAQClient({ apiKey: "...", organizationSlug: "..." });
const questionsPage = await faq.questions.list({ limit: 100 });
const categoriesPage = await faq.categories.list();
return (
<FaqList
questions={questionsPage.data}
categories={categoriesPage.data}
searchable
showCategories
onQuestionClick={(q) => console.log(q.slug)}
/>
);
}QuestionDetail
Renders a single question with answer, feedback buttons, and related questions:
import { QuestionDetailComponent } from "@faqapp/nextjs/components";
<QuestionDetailComponent
question={questionDetail}
showFeedback
onFeedbackSubmit={async (feedback) => {
await faq.feedback.submit(slug, feedback);
}}
/>Hooks
useFaq
Fetches and manages a paginated list of questions:
"use client";
import { useFaq } from "@faqapp/nextjs/hooks";
import { FaqList } from "@faqapp/nextjs/components";
function FaqWidget() {
const { data, loading, error, refetch, updateFilters } = useFaq({
config: { apiKey: "...", organizationSlug: "..." },
autoFetch: true,
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<FaqList questions={data.questions} />
<p>Page {data.pagination?.page} of {data.totalPages}</p>
<button onClick={() => updateFilters({ page: 2 })}>Next page</button>
</div>
);
}useQuestion
Fetches a single question by slug with feedback support:
"use client";
import { useQuestion } from "@faqapp/nextjs/hooks";
function QuestionPage({ slug }: { slug: string }) {
const { data, loading, error, submitFeedback } = useQuestion({
config: { apiKey: "...", organizationSlug: "..." },
questionSlug: slug,
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>{data.question?.question}</h1>
<div dangerouslySetInnerHTML={{ __html: data.question?.answer ?? "" }} />
<button onClick={() => submitFeedback({ helpful: true })}>👍 Helpful</button>
</div>
);
}useSearch
Search questions with a query string:
"use client";
import { useSearch } from "@faqapp/nextjs/hooks";
function SearchWidget() {
const { data, loading, error, search } = useSearch({
config: { apiKey: "...", organizationSlug: "..." },
});
return (
<div>
<input onChange={(e) => search(e.target.value)} placeholder="Search FAQs…" />
{loading && <p>Searching…</p>}
{data?.results.map((r) => (
<div key={r.id}>{r.question}</div>
))}
</div>
);
}SEO Utilities
Generate JSON-LD FAQPage schema for Google rich results:
import { generateFaqJsonLd, generateFaqSeoData, generateQuestionSeoData } from "@faqapp/nextjs";
// Simple JSON-LD from questions
const jsonLd = generateFaqJsonLd(questions);
// Full SEO data with organization context
const seoData = generateFaqSeoData({
organization,
questions,
baseUrl: "https://example.com",
currentPath: "/faq",
});
// Render in <head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>SSR / SSG Helpers
import {
getFaqStaticProps,
getQuestionStaticProps,
getAllQuestionSlugs,
getAllCategorySlugs,
} from "@faqapp/nextjs";
// In getStaticProps
export async function getStaticProps() {
return getFaqStaticProps({
config: { apiKey: "...", organizationSlug: "..." },
filters: { limit: 50 },
});
}
// In getStaticPaths
export async function getStaticPaths() {
const slugs = await getAllQuestionSlugs({
config: { apiKey: "...", organizationSlug: "..." },
});
return {
paths: slugs.map((slug) => ({ params: { slug } })),
fallback: "blocking",
};
}Sitemap Generation
import { generateFaqSitemap, generateSitemapXml } from "@faqapp/nextjs";
const entries = generateFaqSitemap({
questions,
categories,
baseUrl: "https://example.com",
basePath: "/faq",
});
const xml = generateSitemapXml(entries);License
MIT
