# FAQ Page

> Create a frequently asked questions page to reduce support load and improve SEO with structured data.

*Canonical: https://makerkit.dev/docs/nextjs-prisma/marketing-pages/faq*

---

The FAQ page reduces support tickets by answering common questions before users contact you. MakerKit includes a ready-to-use FAQ page at `/faq` with native HTML accordions, JSON-LD structured data for SEO rich results, and i18n support. Questions are stored in translation files, making them easy to update and translate.

The FAQ page is a structured list of frequently asked questions with expandable answers, optimized for search engine rich results and support ticket deflection.

- **Use the FAQ page when:** you have 5-20 common questions that can be answered in 1-3 sentences each, especially billing, account, and getting-started questions.
- **Use documentation when:** answers require step-by-step instructions, code examples, or detailed explanations that exceed a few paragraphs.
- **Use a support system when:** questions require personalized responses, access to user data, or back-and-forth conversation.
  
## File Location

The FAQ page is located at `apps/web/app/[locale]/(public)/faq/page.tsx`.

## Page Structure

The FAQ page uses:
- `SitePageHeader` for the page title and subtitle
- Native HTML `<details>` and `<summary>` elements for the accordion
- JSON-LD structured data for SEO
- A contact CTA button at the bottom

## FAQ Item Component

Each FAQ item uses the native `<details>` element for accessibility and no JavaScript requirement:

```tsx
function FaqItem({
  item,
}: {
  item: {
    question: string;
    answer: string;
  };
}) {
  return (
    <details
      className="hover:bg-muted/70 [&:open]:bg-muted/70 transition-all"
    >
      <summary className="flex items-center justify-between p-4 hover:cursor-pointer">
        <h2 className="cursor-pointer font-sans text-base">
          {item.question}
        </h2>
        <ChevronDown className="h-5 transition duration-300 group-open:-rotate-180" />
      </summary>

      <div className="text-muted-foreground flex flex-col gap-y-2 px-4 pb-2">
        {item.answer}
      </div>
    </details>
  );
}
```

## Translations

FAQ content is stored in translation files for internationalization. Add your questions and answers to the translation file `apps/web/i18n/messages/en/marketing.json`:

```json
{
  "faq": "FAQ",
  "faqSubtitle": "Frequently asked questions about the platform",
  "contactFaq": "If you have any questions, please contact us",
  "faqQuestion1": "Do you offer a free trial?",
  "faqAnswer1": "Yes, we offer a 14-day free trial. You can cancel at any time during the trial period and you won't be charged.",
  "faqQuestion2": "Can I cancel my subscription?",
  "faqAnswer2": "You can cancel your subscription at any time. You can do this from your account settings.",
  "faqQuestion3": "Where can I find my invoices?",
  "faqAnswer3": "You can find your invoices in your account settings.",
  "faqQuestion4": "What payment methods do you accept?",
  "faqAnswer4": "We accept all major credit cards and PayPal.",
  "faqQuestion5": "Can I upgrade or downgrade my plan?",
  "faqAnswer5": "Yes, you can upgrade or downgrade your plan at any time. You can do this from your account settings.",
  "faqQuestion6": "Do you offer discounts for non-profits?",
  "faqAnswer6": "Yes, we offer a 50% discount for non-profits. Please contact us to learn more."
}
```

For complete i18n setup, see [Internationalization](../internationalization/overview).

## Adding FAQ Items

To add more FAQ items:

Add the question and answer translations to `marketing.json`:

```json
{
  "faqQuestion7": "Your new question?",
  "faqAnswer7": "Your answer here."
}
```

Add the item to the `faqItems` array in the page:

```tsx
const faqItems = [
  // ... existing items
  {
    question: t('faqQuestion7'),
    answer: t('faqAnswer7'),
  },
];
```

## SEO Structured Data

The page includes JSON-LD structured data for better search engine visibility. This helps search engines display FAQ rich results (expandable answers directly in search results):

```tsx
const structuredData = {
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: faqItems.map((item) => ({
    '@type': 'Question',
    name: item.question,
    acceptedAnswer: {
      '@type': 'Answer',
      text: item.answer,
    },
  })),
};
```

The structured data is rendered as a script tag:

```tsx
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
```

## Page Metadata

The page includes proper metadata for SEO:

```tsx
export async function generateMetadata(): Promise<Metadata> {
  const t = await getTranslations('marketing');

  return {
    title: t('faq'),
    description: t('faqSubtitle'),
  };
}
```

## SitePageHeader Component

The `SitePageHeader` component provides a consistent header style across marketing pages:

```tsx
import { SitePageHeader } from '../_components/site-page-header';

<SitePageHeader
  title={t('faq')}
  subtitle={t('faqSubtitle')}
/>
```

This component is located at `apps/web/app/[locale]/(public)/_components/site-page-header.tsx`.

## Customization

### Styling the Accordion

Modify the `FaqItem` component to change the accordion appearance:

```tsx
<details className="hover:bg-muted/70 [&:open]:bg-muted/70 transition-all">
  {/* Customize hover and open states */}
</details>
```

### Grouping by Category

To group FAQs by category, restructure the data:

```tsx
const faqCategories = [
  {
    category: 'Billing',
    items: [
      { question: t('faqQuestion1'), answer: t('faqAnswer1') },
      { question: t('faqQuestion2'), answer: t('faqAnswer2') },
    ],
  },
  {
    category: 'Account',
    items: [
      { question: t('faqQuestion3'), answer: t('faqAnswer3') },
    ],
  },
];
```

Then render with category headers:

```tsx
{faqCategories.map((category) => (
  <div key={category.category}>
    <h2 className="text-xl font-semibold mb-4">{category.category}</h2>
    <div className="divide-y divide-dashed rounded-md border">
      {category.items.map((item, index) => (
        <FaqItem key={index} item={item} />
      ))}
    </div>
  </div>
))}
```

## Common Pitfalls

- **Too many FAQs**: More than 20 questions becomes overwhelming. Move detailed topics to documentation and keep FAQs short.
- **Long answers**: FAQ answers should be 1-3 sentences. If an answer needs paragraphs, link to documentation instead.
- **Duplicate structured data**: Only one FAQPage schema per page. Don't combine with other FAQ components that also inject schema.
- **Outdated answers**: Review FAQs quarterly. Outdated pricing or feature information damages trust.
- **Missing translation keys**: Adding a question to the page without the translation key causes a missing translation error. Add both together.
- **Generic questions**: "How does it work?" is too vague. Be specific: "How do I invite team members?"
- **No contact fallback**: Always include a contact CTA at the bottom for questions not covered.

## Related Documentation

- [Internationalization](../internationalization/overview) - Multi-language FAQ support
- [Documentation](./documentation) - Detailed guides for complex topics
- [Landing Page](./landing-page) - Marketing page components

{% faq
   title="Frequently Asked Questions"
   items=[
     {"question": "How many FAQs should I include?", "answer": "Start with 5-10 of your most common support questions. Add more as patterns emerge, but keep the total under 20 for readability."},
     {"question": "Do FAQ rich results always appear in Google?", "answer": "No. Google decides when to show rich results based on relevance and quality. Structured data makes you eligible but doesn't guarantee display."},
     {"question": "Can I use Markdown in FAQ answers?", "answer": "The default implementation uses plain text. For rich formatting, modify the FaqItem component to render Markdown or HTML."},
     {"question": "How do I track which FAQs are most viewed?", "answer": "Add click tracking to the details element's toggle event. Send events to your analytics provider when users expand answers."},
     {"question": "Should I duplicate FAQ content in documentation?", "answer": "Avoid duplication. Link from FAQ answers to detailed documentation pages instead of repeating content."}
   ]
/%}

---

**Next:** [Legal Pages →](./legal-pages)
