Skip to main content
Copy for LLM

FAQ

FAQ stands for "frequently asked questions" — the list of things people keep asking, answered once in the place they will look.

Structurally it is an accordion, and WireKit already has a good one, so this component uses it rather than growing a second. What it adds is the part an accordion cannot know: that these rows are questions, and that search engines should be told so.

FAQ vs Accordion vs Collapsible — when to use which
  • FAQ (this component) — a question list built on the accordion that also emits FAQPage structured data derived from what it renders.
  • Accordion — the general coordinated group of panels (single or multiple open, card chrome) with no question semantics or schema.
  • Collapsible — a single independent show/hide, or a stack of them with no coordination.

<x-wirekit::faq> emits FAQPage structured data built from the questions it just rendered — from the visible answer, never a second hand-written copy. Normally that duplication is your problem: you write the accordion, then you write the JSON-LD again, and the two drift apart on the first copy edit.

Here they cannot drift, because there is only one copy. Reword a question and the schema follows. Delete one and it leaves the schema too. The schema is made out of the visible answer rather than a copy of it.

FAQ with structured data

Look at the page source of that preview and you will find a <script type="application/ld+json"> block listing exactly those three questions — no second list to maintain.

One FAQPage per page

A page should carry exactly one FAQPage. Two of them compete rather than combine, and search engines may ignore both.

So if you show more than one FAQ block on a page, let one of them own the schema and turn it off on the others:

{{-- The main FAQ owns the schema --}}
<x-wirekit::faq>
    <x-wirekit::faq-item question="How do I install it?">…</x-wirekit::faq-item>
</x-wirekit::faq>

{{-- A second block on the same page: same questions, no competing schema --}}
<x-wirekit::faq :schema="false">
    <x-wirekit::faq-item question="Billing question">…</x-wirekit::faq-item>
</x-wirekit::faq>

Turning the schema off never changes what the reader sees — only what search engines are told.

Do not describe answers you are not showing

Only emit the schema where the questions and answers are really on the page. An FAQPage describing content the reader cannot reach is a search-engine policy violation, not a shortcut to rich results. This component keeps you on the right side of that by construction — but if you hide the questions behind something the crawler cannot open, turn schema off.

Rich answers

An answer is a slot, so it takes real markup — links, lists, emphasis:

<x-wirekit::faq-item question="Where are the docs?">
    See <a href="https://docs.wirekit.app">the documentation</a>, or run
    <code>php artisan wirekit:list</code> to browse every component from your terminal.
</x-wirekit::faq-item>

The same markup goes into the schema, because the schema is the rendered answer.

When the markup is too rich for the schema

The schema is read outside the page, by tools that take its text rather than its markup. An answer built from nested components — anything that renders data-* attributes, Alpine directives, or utility classes — carries all of that into the schema, where none of it means anything, even though it reads perfectly on the page.

plain-text strips the markup from the schema only. The reader still sees the full rendered answer; the schema gets the same words as text:

<x-wirekit::faq plain-text>
    <x-wirekit::faq-item question="What is included?">
        <x-wirekit::badge>New</x-wirekit::badge> Priority support
    </x-wirekit::faq-item>
</x-wirekit::faq>

The schema answer becomes Priority support prefixed by the badge's text — entities decoded, whitespace collapsed. It stays derived from what you rendered, so the indexed answer can never drift away from the visible one.

For the rare case where the schema answer must differ in content rather than markup, a single item can override it outright:

<x-wirekit::faq-item question="How do refunds work?" schema-answer="Refunds are issued within 14 days.">
    …the long, linked, formatted answer the reader sees…
</x-wirekit::faq-item>

Reach for schema-answer sparingly: it is hand-written copy, and hand-written copy is what drifts.

Appearance

faq defaults to the accordion's flush variant and lg size, because an FAQ almost always sits inline in page content where the surrounding section already draws the chrome — a second border there only competes with it. Pass variant or size to override; they go straight through to the accordion. Use the boolean multiple prop to let more than one entry stay open at once (faq exposes it instead of the accordion's mode string).

separated, md, and more than one answer open

Open both entries above: multiple is what keeps the first one from closing when the second opens. Against the default flush preview higher up the page, the separated variant is the visible difference — each entry carries its own border instead of sharing one rule between neighbors.

Props

<x-wirekit::faq>

Prop Type Default Description
label string 'Frequently asked questions' Accessible name for the question list.
variant string 'flush' Passed to the accordion: flush, bordered, separated.
size string 'lg' Passed to the accordion: md, lg.
multiple bool false Let several answers stand open at once. Default is one at a time.
findable bool true Passed to the accordion: the browser's find in page searches closed answers and opens the one a match lands in. See Accordion — Find in Page.
animate bool true Passed to the accordion: an answer eases its height open and shut. Skipped under prefers-reduced-motion: reduce. See Accordion — Animation.
schema bool true Emit FAQPage JSON-LD for the questions rendered here.
plainText bool false Strip markup from the schema answer text (entities decoded, whitespace collapsed). The visible answer is untouched.
scope string|null null Class-scope override.

<x-wirekit::faq-item>

Prop Type Default Description
question string '' The question. Rendered as the trigger and recorded for the schema.
schemaAnswer string|null null Replace this item's schema answer outright, for the rare case where it must differ from the visible one in content. Prefer plain-text on the faq.
id string|null null Explicit item id. Generated when omitted.
scope string|null null Class-scope override.

Accessibility

The accordion's accessibility is inherited whole, not reimplemented: each question is a real <button> inside a heading with aria-expanded and aria-controls, and each answer is a role="region" labeled by its question. Keyboard support comes from the same place.

An aria-label or aria-labelledby of yours names the group of questions in place of label.

An empty question or an empty answer is dropped from the schema rather than emitted as a node with no name — a Question with nothing to ask is not something to tell a search engine about.

Keyboard Interaction

Inherited from accordion — Tab moves between questions, Enter / Space opens and closes the focused one.

Structured data & AI search (GEO)

Who reads this schema now — LLMs and AI answer engines

The FAQPage markup is consumed mostly by LLM-based answer engines and AI crawlers today — the AI agents and AI search overviews that read structured Q&A to extract and cite answers. That is generative engine optimization (GEO), and FAQ is among the schema types those engines cite most often; being machine-readable, it reaches an AI agent that only sees your HTML and cannot run the JavaScript that reveals the accordion. Google's own FAQ rich result, by contrast, is retired — narrowed to authoritative government and health sites in 2023, then removed in 2026 — so the payoff today is visibility in AI answers plus a schema built from the visible answer, not a Google rich result.

Updated in WireKit v2.65.0 (2026-10-05)

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store