React

An accordion for FAQs, with the right schema

A single array of questions feeds both Accordion and FAQPage JSON-LD. Answers must sit in the HTML: if they only appear on click, crawlers never read them.

Key points

  • One array of five questions feeds both the `Accordion` and the `FAQPage` JSON-LD: two copies always drift.
  • Radix unmounts closed content: without `forceMount` or answers already in the HTML, crawlers see nothing.
  • FAQ markup does not give rich results on every site: since 2023 Google shows them only to authoritative government and health sites.

Short answer: one array of questions and answers feeds both the visible Accordion and the FAQPage JSON-LD. The answer text must be in the page HTML: if it exists only after the click, the crawler reads an empty accordion.

Single or multiple

For an FAQ use type="single" with collapsible: one answer at a time, the page stays short and the user does not lose their place. type="multiple" only makes sense when answers are meant to be compared, for instance two pricing tiers side by side.

npx shadcn@latest add accordion

One array, two outputs

"use client";

import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion";

export type Faq = { question: string; answer: string };

export const faq: Faq[] = [
  { question: "Serve JavaScript?", answer: "No: le risposte sono gia' nell'HTML." },
  { question: "Quante domande?", answer: "Cinque, le stesse del JSON-LD." },
];

export function FaqAccordion() {
  return (
    
      {faq.map((item, index) => (
        
          {item.question}
          {item.answer}
        
      ))}
    
  );
}
import { faq } from "@/app/faq/faq";

export const faqJsonLd = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  mainEntity: faq.map((item) => ({
    "@type": "Question",
    name: item.question,
    acceptedAnswer: { "@type": "Answer", text: item.answer },
  })),
};

The point that breaks everything: unmounted content

Radix keeps no closed panel in the HTML: it mounts the content on open and unmounts it on close. A crawler that does not click finds the titles and nothing else. The two ways out are forceMount on the content, which stays in the DOM with the hidden attribute, or a block of answers that is visible without JavaScript.

ChoiceCrawlerUser
Accordion without forceMountReads the questions onlyClean experience
forceMount on the contentReads questions and answersSame answers, content in the DOM
Answers always visible, accordion as an extraReads everythingLonger page

Source: the schema.org FAQPage specification and the shadcn/ui Accordion pattern, both MIT licensed for the code; the rule about visible content comes from Google's structured data guidelines, not from a preference of ours.

A named limitation

The markup does not lift rankings and does not guarantee rich results: since 2023 Google shows FAQ rich results only for authoritative government and health sites, and for everyone else the JSON-LD helps machines understand the page rather than decorate the SERP. A second limit: marking up questions that are not visible on the page breaks the guidelines, so the match between what is shown and what is declared has to be kept by hand, on every edit.

Where to continue

In the catalogue you find the shadcn library, an accordion block with preview and the faq category. The full guide to the markup is FAQPage schema and the overview is in how to add schema markup; the roundup of blocks is the best FAQ blocks of 2026. The comparison between component libraries is shadcn versus Mantine; for the colours of a status accordion read status badges.

Frequently asked questions

Is a closed accordion panel in the HTML?

No, Radix mounts it only on open. To keep it in the DOM add `forceMount` to `AccordionContent`, and know that the content stays hidden through the `hidden` attribute.

Do I have to write the questions twice, once for the markup?

No. One exported array feeds the component and the function that builds the JSON-LD: two copies drift on the first edit.

Where does the JSON-LD go in the App Router?

In a `script` tag with `type="application/ld+json"` inside the page server component, with `JSON.stringify`. No client component is needed.

How many questions go into the schema?

The same ones visible on the page, five in this article. Extra questions in the schema and not on the page break the guidelines.

Does FAQ markup give rich results on Google?

Not in general. Since 2023 FAQ rich results are limited to authoritative government and health sites: for everyone else the markup stays useful but changes nothing in the SERP.