thefaqapp
Skip to main content

Next.js SDK — React Hooks & SSR Helpers

Next.js integration for TheFAQApp via @faqapp/nextjs. React hooks, components, SSR/SSG helpers, and SEO utilities for FAQ content.

Next.js SDK (@faqapp/nextjs)

React hooks, components, SSR/SSG helpers, and SEO utilities for Next.js.

Re-exports everything from @faqapp/core, so you only need one package.

View on npm → · Core SDK on npm →

Install

npm install @faqapp/nextjs

Hooks

useFaq — List & Filter Questions

'use client';

import { useFaq } from '@faqapp/nextjs';

export function FAQList() {
  const { data, loading, error, updateFilters } = useFaq({
    config: {
      apiKey: process.env.NEXT_PUBLIC_FAQ_API_KEY!,
      organizationSlug: 'my-org',
    },
    initialFilters: { limit: 10, category: 'getting-started' },
  });

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <input onChange={e => updateFilters({ search: e.target.value })} />
      {data.questions.map(q => (
        <article key={q.id}>
          <h3>{q.question}</h3>
          <div dangerouslySetInnerHTML={{ __html: q.answer }} />
        </article>
      ))}
    </div>
  );
}

Returns: { data, loading, error, refetch, updateFilters, filters }

  • data.questionsQuestion[]
  • data.pagination{ page, limit, total, pages } | null
  • data.totalPagesnumber

useQuestion — Single Question with Feedback

'use client';

import { useQuestion } from '@faqapp/nextjs';

export function QuestionPage({ slug }: { slug: string }) {
  const { data, loading, submitFeedback } = useQuestion({
    config: { apiKey: '...', organizationSlug: 'my-org' },
    questionSlug: slug,
  });

  if (loading || !data.question) return null;

  return (
    <div>
      <h1>{data.question.question}</h1>
      <div dangerouslySetInnerHTML={{ __html: data.question.answer }} />
      <button onClick={() => submitFeedback({ helpful: true })}>👍</button>
      <button onClick={() => submitFeedback({ helpful: false })}>👎</button>
    </div>
  );
}

Returns: { data, loading, error, refetch, submitFeedback }

  • data.questionQuestionDetail | null
  • submitFeedback(params) — Submit helpful/not-helpful feedback

useSearch — Search Questions

'use client';

import { useSearch } from '@faqapp/nextjs';

export function SearchBar() {
  const { data, loading, search } = useSearch({
    config: { apiKey: '...', organizationSlug: 'my-org' },
  });

  return (
    <div>
      <input onChange={e => search(e.target.value)} placeholder="Search FAQ..." />
      {data?.results.map(q => (
        <a key={q.id} href={`/faq/${q.slug}`}>{q.question}</a>
      ))}
    </div>
  );
}

Returns: { data, loading, error, search }

  • dataSearchResponse | null with results, query, and total
  • search(query, filters?) — Trigger a search

Server Components

Use @faqapp/core directly in Server Components (re-exported from @faqapp/nextjs):

import { FAQClient } from '@faqapp/nextjs';

export default async function FAQPage() {
  const client = new FAQClient({
    apiKey: process.env.FAQ_API_KEY!,
    organizationSlug: 'my-org',
  });

  const page = await client.questions.list({ limit: 50 });

  return (
    <ul>
      {page.data.map(q => <li key={q.id}>{q.question}</li>)}
    </ul>
  );
}

Pre-built Components

<FaqList />

Full FAQ list with search and category filtering. Accepts data as props — fetch on the server and pass down.

import { FaqList } from '@faqapp/nextjs';

<FaqList
  questions={questions}
  categories={categories}
  showCategories
  searchable
  onQuestionClick={(q) => router.push(`/faq/${q.slug}`)}
/>

Props:

PropTypeDefaultDescription
questionsQuestion[]requiredQuestions to display
categoriesCategory[][]Categories for grouping/filtering
showCategoriesbooleantrueShow category grouping
searchablebooleantrueShow search input
onQuestionClick(q: Question) => voidClick handler
renderQuestion(q: Question) => ReactNodeCustom question renderer
renderCategory(c: Category) => ReactNodeCustom category renderer

<QuestionDetailComponent />

Question view with feedback buttons and related questions.

import { QuestionDetailComponent } from '@faqapp/nextjs';

<QuestionDetailComponent
  question={questionDetail}
  relatedQuestions={related}
  showFeedback
  onFeedbackSubmit={async (feedback) => {
    await client.feedback.submit(slug, feedback);
  }}
/>

Props:

PropTypeDefaultDescription
questionQuestionDetailrequiredQuestion to display
relatedQuestionsQuestion[][]Related questions
showFeedbackbooleantrueShow feedback buttons
showRelatedbooleantrueShow related questions
onFeedbackSubmit(feedback) => Promise<void>Feedback handler

SEO Utilities

FAQ Structured Data (JSON-LD)

import { generateFaqJsonLd, generateFaqSeoData } from '@faqapp/nextjs';

// Simple JSON-LD from questions array
const jsonLd = generateFaqJsonLd(questions, { name: 'My FAQ', url: 'https://example.com/faq' });

// Full SEO data with organization context
const seoData = generateFaqSeoData({
  organization,
  questions,
  baseUrl: 'https://example.com',
  currentPath: '/faq',
});
// Returns: { title, description, canonical, openGraph, jsonLd }

Question Structured Data

import { generateQuestionSeoData } from '@faqapp/nextjs';

const seoData = generateQuestionSeoData({
  organization,
  question,
  baseUrl: 'https://example.com',
  currentPath: `/faq/${question.slug}`,
});

Sitemap Generation

import { generateFaqSitemap, generateSitemapXml } from '@faqapp/nextjs';

const entries = generateFaqSitemap({
  questions,
  categories,
  baseUrl: 'https://example.com',
  basePath: '/faq',
});

const xml = generateSitemapXml(entries);

Static Generation Helpers

Helpers for getStaticProps and getStaticPaths patterns:

import {
  getFaqStaticProps,
  getQuestionStaticProps,
  getAllQuestionSlugs,
  getAllCategorySlugs,
} from '@faqapp/nextjs';

// Get all question slugs for static paths
const slugs = await getAllQuestionSlugs({ config });

// Get props for FAQ listing page
const { props, revalidate } = await getFaqStaticProps({
  config,
  filters: { status: 'published' },
});

// Get props for a single question page
const { props } = await getQuestionStaticProps({
  config,
  questionSlug: 'how-to-reset',
});