Building in Public#building-in-public

Building FAQPage Schema for Google Rich Results in Next.js

By Mohamed Abdi Guled

FAQPage schema passing Google's validator does not guarantee the rich result appears — Google has significantly narrowed eligibility for it.

Building FAQPage Schema for Google Rich Results

ShabelleHub has a FAQ page. For most sites, a FAQ page is just content — questions and answers formatted in a readable layout. With the right structured data, it becomes *eligible* for rich results in Google Search: expandable question-and-answer blocks that can appear directly in the search results page, above the regular blue links.

This is how we built the FAQ page and added the FAQPage schema — and, just as important, what changed in Google's eligibility rules that means passing validation is no longer the same as earning the rich result.


⚠️ Important Update: Eligibility Has Narrowed

Before the implementation details: Google has significantly tightened eligibility for FAQ rich results. As of recent policy changes, the visual rich result is now scoped primarily to well-known, authoritative government and health sites. A general content or product site — ShabelleHub included — can implement FAQPage schema with zero errors and still never see the expandable accordion in search results, because the feature itself has been scoped down at the search-result level, not because anything is wrong with the markup.

This doesn't mean the schema is pointless. It's still worth implementing correctly, because it helps Google's crawler understand the page's content structure even without the visual rich result, and because eligibility criteria change over time. The rest of this article is the correct implementation — treat the rich result itself as a possible bonus, not a guarantee.


Why Rich Results Matter (When They Show)

When Google recognises valid FAQPage structured data on an eligible page, it may display the questions and answers directly in search results as expandable accordion items. A result with expandable FAQ items takes up significantly more screen space than a standard blue link, which increases the probability that a user notices and engages with it — on the sites the feature is currently scoped to.


The Data Structure

We defined the FAQ content as a plain array of objects in the page file:

// pages/faq.js
const FAQS = [
  {
    q: 'How does Shabelle Hub choose which AI tools to review?',
    a: `We focus on AI tools that are widely used or solve a clear, common
        problem — general assistants, coding tools, image and video generation,
        research tools, voice AI, and productivity add-ons. We currently cover
        ${toolsCount} tools across ${categoriesCount} categories, and we are
        adding more as we test them.`,
  },
  {
    q: 'Are your reviews really independent?',
    a: 'Yes. We do not accept payment for inclusion or for positive coverage. Some of our links are affiliate links, which means we may earn a commission if you sign up through them — but this never determines which tools we cover or how we rate them.',
  },
  {
    q: 'How do you test each tool?',
    a: 'Every tool is signed up for and used hands-on across realistic tasks relevant to its category. We test the free tier where one exists, then the paid tier, and note specific strengths and limitations we encounter. Ratings reflect this testing, not vendor marketing claims.',
  },
  {
    q: 'How often is content updated?',
    a: "AI tools change quickly — pricing, features, and limits can shift with little notice. We aim to revisit and update our reviews periodically, but always check the provider's official site for the most current pricing before subscribing.",
  },
  {
    q: 'I think a review is inaccurate or outdated. How can I let you know?',
    a: 'Please use our Contact page to let us know. We review all feedback and update pages when something has genuinely changed.',
  },
  {
    q: 'Can I suggest a tool for you to review?',
    a: "Absolutely — send suggestions through the Contact page. We can't review everything, but we do prioritise tools that readers ask about most.",
  },
];

toolsCount and categoriesCount are imported from the tools catalog and used directly in the answer text — the same derived-stats pattern used everywhere else on the site, so the FAQ answer always reflects the current catalog size automatically.


The JSON-LD Schema

export function getFaqStructuredData(faqs) {
  return {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    mainEntity: faqs.map((faq) => ({
      '@type': 'Question',
      name: faq.question,
      acceptedAnswer: {
        '@type': 'Answer',
        text: faq.answer,
      },
    })),
  };
}

The structure is straightforward:

  • The top-level type is FAQPage
  • Each question is a Question entity with a name (the question text)
  • Each answer is an Answer entity nested inside acceptedAnswer
  • The text field of the Answer should be plain text matching the visible answer — HTML tags or a reworded/truncated summary written specifically for the schema causes validation warnings and, more importantly, is treated as divergence from visible content (see below)

Injecting the Schema Into the Page

export default function FAQPage() {
  const faqJsonLd = {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    mainEntity: FAQS.map(f => ({
      '@type': 'Question',
      name: f.q,
      acceptedAnswer: { '@type': 'Answer', text: f.a },
    })),
  };

  return (
    <>
      <NextSeo
        title="Frequently Asked Questions"
        description="Answers to common questions about Shabelle Hub's AI tool reviews, our testing process, and editorial independence."
        canonical="https://shabellehub.com/faq"
      />

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

      <div>
        <h1>Frequently Asked Questions</h1>
        {FAQS.map((f, i) => (
          <div key={i}>
            <h2>{f.q}</h2>
            <p>{f.a}</p>
          </div>
        ))}
      </div>
    </>
  );
}

Two things worth noting:

dangerouslySetInnerHTML is required for injecting raw JSON into a script tag. React escapes HTML by default, which would corrupt the JSON. The use is intentional and safe here because we are serialising our own data, not user input.

The visible HTML mirrors the schema exactly. The <h2> elements contain the same question text as the name field. The <p> elements contain the same answer text as the text field, word for word — not a summary. This match is not a stylistic nicety; it's the actual thing Google checks for underneath the eligibility rules.


What the Schema Must Not Do

Google's FAQ structured-data policies have specific, strictly-enforced requirements — and a validator checks none of them:

The questions and answers must be visible on the page, in substantially the same content the schema describes. Schema describing FAQ content that isn't rendered anywhere on the visible page is treated as an attempt to manipulate search results — not a legitimate FAQ section. A validator will happily accept this. It will still fail to earn the rich result, or worse, contribute to a manual action.

The answer text needs to match what a user actually sees, not a truncated or reworded summary written specifically for the schema. Divergence between the schema's text field and the rendered answer is treated the same as invisible content.

Do not use FAQPage schema for advertising. Questions like "Why should I choose ShabelleHub?" with purely promotional answers are against policy. The questions should address genuine user needs.

Do not duplicate FAQPage schema across many pages. If the same FAQ content appears on multiple pages, only one of them should carry the schema. Duplicating it signals low-quality content.


Verifying With the Rich Results Test

After deploying the FAQ page, we ran it through Google's Rich Results Test at search.google.com/test/rich-results.

The ShabelleHub FAQ page passed with no errors — all six questions extracted correctly, the JSON-LD was valid and parseable, and the question/answer text matched the visible page content.

What this test does not confirm: eligibility for the actual visual rich result in search. Passing validation only confirms the JSON-LD is structurally correct. Given the current scope of FAQ rich results toward government and health sites, a technically perfect implementation on a general content site like ShabelleHub may simply never render as an accordion in search — that's a scope decision made at the search-result level, separate from whether the markup is correct.


Adding FAQ Schema to Other Pages

The FAQPage type is specifically for pages whose primary purpose is FAQ content. For other page types that include a small FAQ section — like a tool review page with common questions about that tool — a set of Question entities nested within the page's primary schema is the more correct approach, rather than a standalone FAQPage. This is outside the scope of what ShabelleHub currently implements.


The Result

The ShabelleHub FAQ page:

  • Has a dedicated URL at /faq
  • Is included in the sitemap with changefreq: monthly
  • Is linked from the footer
  • Passes the Rich Results Test with no errors
  • Uses derived stats so tool and category counts in answers stay accurate automatically

The structured data cost nothing to add and required no third-party library — a handful of lines mapping an existing data array to a schema format. What it doesn't cost is also what it doesn't guarantee: given Google's current eligibility scope, this is implemented as correct groundwork rather than a guaranteed search-results feature.


Frequently Asked Questions

If FAQPage schema passes Google's Rich Results Test, will it show up in search?

Not necessarily. Passing validation only confirms the JSON-LD is structurally correct — it doesn't confirm eligibility. Google has scoped FAQ rich results down significantly, largely toward authoritative government and health sites.

Does the FAQ content need to be visible on the page itself?

Yes, and this is non-negotiable. Schema describing FAQ content that doesn't visibly exist on the rendered page is treated as an attempt to manipulate search results, not legitimate structured data.

Can the schema's answer text be a shortened summary of the visible answer?

No. The text field needs to match what a user actually sees. A paraphrased or truncated version written specifically for the schema is treated the same as invisible content.

Is it still worth adding FAQPage schema if the rich result isn't guaranteed?

Yes — it still helps search engines understand your page's content structure, and eligibility rules change over time. Just don't treat the rich result itself as a guaranteed outcome.


*This article is part of the ShabelleHub Building in Public series.*

*Next: Fixing Duplicate H1 Tags in Next.js Markdown Blog Posts*

Frequently Asked Questions

If FAQPage schema passes Google's Rich Results Test, will it show up in search?+
Not necessarily. Passing validation only confirms the JSON-LD is structurally correct. Google has scoped FAQ rich results down significantly, largely toward government and health sites.
Does the FAQ content need to be visible on the page itself?+
Yes. Schema describing FAQ content that does not visibly exist on the rendered page is treated as an attempt to manipulate search results.
Is it still worth adding FAQPage schema if the rich result is not guaranteed?+
Yes — it still helps search engines understand your page's content structure, and eligibility rules change over time.

📬 Get the latest AI tool reviews

Expert picks and comparisons, weekly. No spam.

← Back to Blog