npm package: @faqapp/nextjs
Description: Next.js SDK for TheFAQApp — React hooks, components, SSR/SSG helpers, and SEO utilities
Latest version: 0.1.1

# @faqapp/nextjs

Next.js components, hooks, and SEO utilities for [TheFAQApp](https://www.thefaq.app).

## Install

```bash
npm install @faqapp/core @faqapp/nextjs
# or
pnpm add @faqapp/core @faqapp/nextjs
```

## Components

### FaqList

Renders a searchable, filterable FAQ list with category grouping:

```tsx
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:

```tsx
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:

```tsx
"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:

```tsx
"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:

```tsx
"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:

```tsx
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

```ts
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

```ts
import { generateFaqSitemap, generateSitemapXml } from "@faqapp/nextjs";

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

const xml = generateSitemapXml(entries);
```

## License

MIT