To add FAQ schema to Shopify without an app, create a theme section that outputs your visible questions and a matching FAQPage JSON-LD script from the same fields, add it to an alternate template, then validate the rendered page. Know the 2026 reality before you start: Google stopped showing FAQ rich results in Search on May 7, 2026, so this markup describes your content for machines but will not create dropdowns in Google results.
This guide is for Shopify merchants, developers and SEO leads who want FAQ markup that stays accurate without another app subscription. You'll get the current Google position with dates, a decision table for the four ways to do it, two working Liquid examples (a reusable section and a metaobject version for product FAQs), a testing checklist and the mistakes we see most often in theme code.
Does FAQ schema still matter in 2026?
FAQ schema is FAQPage structured data from schema.org: a JSON-LD block that labels each question and its accepted answer on a page. For years its main appeal was the expandable FAQ rich result in Google. That feature has now been shut down in two steps.
Source: Google Search Central Blog, Changes to HowTo and FAQ rich results (Aug 8, 2023); Google Search Central documentation updates (May 8 and June 15, 2026)
In August 2023 Google said FAQ rich results would "only be shown for well-known, authoritative government and health websites." On May 8, 2026 Google's documentation changelog added a deprecation notice saying the feature "will no longer appear in Google Search starting May 7, 2026," and on June 15, 2026 Google removed the FAQ rich result documentation entirely. Our guide to why FAQ schema is not showing in Google covers the history in more detail.
So why build it at all?
- The visible FAQ is the real asset. Clear answers about sizing, shipping, returns and compatibility reduce support tickets and help shoppers decide. In our own testing, an FAQ accordion won every one of 19 A/B tests where we added it to homepages and collections.
- Markup is cheap when it shares a source. If the section that prints the answers also prints the JSON-LD, there is no second copy to keep in sync.
- Unused markup is harmless. When Google limited the feature in 2023, it said "there's no need to proactively remove it" and that structured data that's not being used "does not cause problems for Search."
What FAQ markup does not do: it does not guarantee rankings, AI Overview inclusion or ChatGPT citations. Google's structured data guidelines state that it "does not guarantee that your structured data will show up in search results." No primary source we could find says AI answer engines reward FAQPage markup specifically, so treat any vendor claim that it does with caution. Clear, quotable answers in the visible HTML are what help most. Our pillar guide on FAQ schema explains the vocabulary itself.
Choose how to add FAQ schema to Shopify
There are four common ways to get FAQPage JSON-LD onto a Shopify page. The right one depends on whether the questions are the same across many pages or unique to each product.
| Method | Best for | Visible FAQ and markup in sync? | Main risk |
|---|---|---|---|
| Theme section with blocks (Method 1) | A dedicated FAQ page, shipping or returns page, a single landing page | Yes, both come from the same block fields | Section settings are shared by every page on the same template |
| Metaobjects plus a product metafield (Method 2) | Product-specific questions across a catalog | Yes, both come from the same metaobject entries | Needs a metaobject definition and a little developer time |
| FAQ or SEO app | Teams with no theme access | Usually, if the app renders both | Duplicate FAQPage output if the theme also emits it; monthly fee |
| Hand-pasted JSON-LD in a Custom Liquid block | One-off pages | No, you maintain two copies | Answers drift from the visible text after edits |
For most stores we recommend Method 1 for policy and support pages and Method 2 for product pages. Both keep a single source of truth, which is the only thing that keeps markup accurate a year from now. If you want to see what correct output looks like before touching code, build a sample with our FAQ Schema Generator and keep it as a reference.
Before you start: back up and check for duplicates
Theme edits go live the moment you save in the published theme, so work in a copy. Shopify's help center explains how to duplicate a theme from Online Store > Themes using the actions menu next to the theme. Edit and preview the duplicate, then publish it when everything checks out.
Find any FAQPage markup that already exists
Many themes, review apps and SEO apps already output structured data. Two FAQPage blocks describing the same questions is the most common FAQ schema mistake we find. Open the page you plan to edit, open your browser's developer console, and run:
[...document.querySelectorAll('script[type="application/ld+json"]')]
.filter(s => s.textContent.includes('FAQPage')).length
A result of 0 means no FAQPage JSON-LD is present in the rendered page. Anything higher means something already outputs it. Also search the page source for FAQPage to catch microdata (an itemtype attribute) and entities nested inside an @graph. Then check these places in the theme code editor and admin:
- Sections and snippets with names like
faq,collapsible-contentoraccordion - Custom Liquid blocks added in the theme editor
- App embeds under Theme settings > App embeds for SEO, schema and FAQ apps
Pick one owner for FAQ output. If an app already prints accurate FAQPage markup from your visible answers, you may not need any code. If you switch to a theme section, turn off only the app's FAQ feature, not its Product or BreadcrumbList output.
We'll benchmark your store against your top three competitors on Google and AI answers, flag the pages ready to move and prioritize the technical fixes with the revenue math.
Method 1: an FAQ section with built-in JSON-LD
This section prints a visible accordion and the matching FAQPage script from the same question and answer blocks. It skips any block with an empty field and outputs nothing at all when no complete questions exist, so you never ship an empty mainEntity array.
- In your duplicate theme, go to Online Store > Themes > ... > Edit code.
- In the
sectionsfolder, add a new file namedfaq-with-schema.liquidand paste the code below. - Under
templates, create an alternate page template such aspage.faq. Online Store 2.0 templates are JSON files that list sections, so you add the section through the theme editor rather than pasting Liquid into the JSON file. - Open the theme editor, switch to the
page.faqtemplate, add the FAQ with schema section and fill in your questions and answers. - In the Shopify admin, assign the
faqtemplate to the page you want, then preview it in the duplicate theme.
{%- liquid
assign faq_count = 0
for block in section.blocks
assign q = block.settings.question | strip
assign a = block.settings.answer | strip
if q != blank and a != blank
assign faq_count = faq_count | plus: 1
endif
endfor
-%}
{%- if faq_count > 0 -%}
<section aria-labelledby="faq-{{ section.id }}">
<h2 id="faq-{{ section.id }}">{{ section.settings.heading | escape }}</h2>
{%- for block in section.blocks -%}
{%- assign q = block.settings.question | strip -%}
{%- assign a = block.settings.answer | strip -%}
{%- if q != blank and a != blank -%}
<details {{ block.shopify_attributes }}>
<summary>{{ q | escape }}</summary>
<p>{{ a | escape | newline_to_br }}</p>
</details>
{%- endif -%}
{%- endfor -%}
</section>
{%- assign emitted = 0 -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{%- for block in section.blocks -%}
{%- assign q = block.settings.question | strip -%}
{%- assign a = block.settings.answer | strip -%}
{%- if q != blank and a != blank -%}
{%- if emitted > 0 -%},{%- endif -%}
{
"@type": "Question",
"name": {{ q | json | replace: '<', '\u003c' }},
"acceptedAnswer": {
"@type": "Answer",
"text": {{ a | json | replace: '<', '\u003c' }}
}
}
{%- assign emitted = emitted | plus: 1 -%}
{%- endif -%}
{%- endfor -%}
]
}
</script>
{%- endif -%}
{% schema %}
{
"name": "FAQ with schema",
"settings": [
{ "type": "text", "id": "heading", "label": "Heading", "default": "Frequently asked questions" }
],
"blocks": [
{
"type": "faq",
"name": "Question",
"settings": [
{ "type": "text", "id": "question", "label": "Question" },
{ "type": "textarea", "id": "answer", "label": "Answer (plain text)" }
]
}
],
"max_blocks": 20,
"presets": [{ "name": "FAQ with schema", "blocks": [{ "type": "faq" }] }]
}
{% endschema %}
Why the code is written this way
- The
jsonfilter adds the quotes. Shopify's Liquid reference says you don't need to wrap its output in quotes because "the json filter includes them," and it escapes quotes inside the text. Adding your own quotes around{{ q | json }}is the most common way this markup breaks. - The
replaceon the less-than sign turns any<in an answer into the JSON escape\u003c, so a stray</script>typed into an answer cannot close the script tag early. JSON parsers read it back as the original character. - Plain-text fields. The answer uses a
textareasetting so the visible text and the JSON-LD text match exactly. If you need links in answers, switch to arichtextsetting, print it unescaped in the HTML, and run it throughstrip_htmlbeforejsonin the script. - The
{% schema %}tag is not structured data. It only configures the theme editor. The schema.org markup is theapplication/ld+jsonscript. - Body placement is fine. Google's structured data introduction says JSON-LD can sit in a script tag in the head or the body of the page.
Because the questions are stored in the template's section settings, every page assigned to page.faq shows the same FAQ. If your shipping page and your wholesale page need different answers, give each its own alternate template, or use Method 2.
What the rendered output should look like
With two questions entered, view the page source of the preview and you should see a script like this, with no trailing comma and no doubled quotes:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Do you ship to Canada?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. Orders to Canada ship in 3 to 5 business days. Duties are shown at checkout."
}
},
{
"@type": "Question",
"name": "Can I return a sale item?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Sale items can be returned within 30 days for store credit."
}
}
]
}
Method 2: product FAQs with metaobjects
Template-level sections fall apart on product pages, because every product on the default product template would share the same answers. Store product questions on the product itself instead. Shopify's Liquid reference says the value property of a list metafield "returns an array" you can loop through, and metaobject fields are read with .value, which is the pattern below.
Set up the data model
- In Settings > Custom data > Metaobjects, add a definition named FAQ item with two fields:
question(single line text) andanswer(multi-line text). - In Settings > Custom data > Products, add a metafield definition with namespace and key
custom.faqs, type Metaobject, referencing FAQ item, set to a list of entries. - On each product, create or pick the FAQ entries that apply. A question like "Is this compatible with the 2024 model?" can be reused on every product it applies to.
- Add a new section file, for example
sections/product-faq.liquid, with the code below plus a{% schema %}tag and preset like Method 1, then add it to your product template in the theme editor.
{%- assign faqs = product.metafields.custom.faqs.value -%}
{%- if faqs != blank -%}
<section aria-label="Product questions">
<h2>Questions about {{ product.title | escape }}</h2>
{%- for item in faqs -%}
<details>
<summary>{{ item.question.value | escape }}</summary>
<p>{{ item.answer.value | escape | newline_to_br }}</p>
</details>
{%- endfor -%}
</section>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{%- for item in faqs -%}
{
"@type": "Question",
"name": {{ item.question.value | json | replace: '<', '\u003c' }},
"acceptedAnswer": {
"@type": "Answer",
"text": {{ item.answer.value | json | replace: '<', '\u003c' }}
}
}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
}
</script>
{%- endif -%}
Products with no FAQ entries print nothing, so the section is safe to add to the default product template. Forum reports suggest Liquid loops over list references stop at 50 entries; keep product FAQs well below that anyway. Five to eight genuine questions is plenty for a product page.
| Page type | Good FAQ topics | Where to store it |
|---|---|---|
| Product page | Sizing, compatibility, materials, care, what's in the box | Metaobject entries on a product list metafield |
| Collection page | How to choose between products in the range | Collection metafield or a section on an alternate collection template |
| FAQ or help page | Shipping, returns, payment, order changes | Section blocks on an alternate page template |
| Homepage | The three to five questions that block first purchases | Section blocks on the index template |
Keep the same question off dozens of unrelated products. Google's guidelines say structured data "must be a true representation of the page content," so a sitewide shipping FAQ belongs on your shipping page, not repeated in every product's markup. If your product pages also carry Product markup, our guide to product schema on Shopify covers that side.
Why the visible FAQ matters more than the markup
With the rich result gone, the business case for FAQs rests on shoppers. That case is strong in our own data. Across 197 A/B tests on 26 e-commerce stores, tests in FAQ sections won every time.
View the data
| Value | |
|---|---|
| FAQ sections | 100% |
| Homepage | 94.1% |
| Product pages | 87.1% |
| Sitewide | 85.2% |
| Collections | 81% |
Source: QCK A/B test results, 197 tests, qck.co/ab-test-results
Those wins came from the content and its placement, not from schema. So write the answers first:
- Answer in the first sentence. "Yes, it fits the 2024 model" beats a paragraph of context. Short, direct answers are also the easiest for AI answer engines to quote.
- Use real questions. Pull them from support tickets, chat logs, reviews and the "People also ask" box for your product category.
- Keep answers in the HTML. An accordion is fine, but load the answer text with the page rather than fetching it when someone clicks.
- Keep it page specific. Five questions that matter on this page beat twenty generic ones.
Our free $100M AI Visibility Roadmap, built from 7,960 live pages and 197 A/B tests, shows how to get your answers cited by ChatGPT, Google AI Overviews and Perplexity.
How to test FAQ schema on Shopify
Test the rendered page, not the Liquid file. A validator cannot run Liquid; it only sees the HTML Shopify outputs.
- Preview the duplicate theme on the page with the FAQ template assigned and view the page source.
- Copy the JSON-LD script and paste it into our Schema Markup Validator or validator.schema.org. Fix any syntax error first; a single extra comma invalidates the whole block.
- Compare every question and answer in the markup with the visible text. They should match word for word.
- Stress-test the fields. Enter an answer with quotation marks, an apostrophe, a line break and a less-than sign. Confirm the script still parses and the page still displays correctly.
- Test the empty states. Clear one answer, then remove all blocks. The incomplete question should disappear from both outputs, and with no blocks the script should not print at all.
- Rerun the duplicate check from earlier. The console count should be exactly 1.
- Check an unrelated page, such as a blog article, to confirm the FAQ did not leak into the wrong template.
- Publish, then recheck the live URL. Template assignments and app settings are not reversed by rolling back a theme, so write down what you changed.
Don't use Google's Rich Results Test as the pass or fail signal for FAQ markup. Google has removed the FAQ feature and its documentation, so a missing FAQ result there says nothing about whether your JSON-LD is valid. A general schema validator is the right check now.
Common FAQ schema mistakes on Shopify
| Symptom | Likely cause | Fix |
|---|---|---|
| Validator reports invalid JSON | Extra quotes around a json filtered value, or a trailing comma after the last question | Remove the manual quotes; use forloop.last or a counter for commas |
| Two FAQPage blocks on one page | An app embed and a theme section both output FAQ markup | Keep one owner and disable the other's FAQ output only |
| Same answers on every product | FAQ blocks added to the shared product template | Move product questions into metaobjects (Method 2) |
| Markup lists questions the page doesn't show | Hand-pasted JSON-LD that was never updated | Generate markup from the same fields as the visible FAQ |
| Answer text shows raw HTML tags in the script | Rich text printed into JSON-LD without strip_html | Strip HTML before the json filter |
| Translated page has English markup | Visible text translated, hardcoded JSON-LD not | Generate both from translatable fields |
If you have other structured data on the page, check that you haven't broken it while editing. Our Schema Markup Generator can rebuild Organization, Product or BreadcrumbList blocks for comparison, and our ecommerce schema markup guide explains which types still produce Google features. It is also worth confirming each FAQ page has the right canonical tag, so the version you marked up is the one Google indexes.
Keep your FAQs accurate after launch
FAQ markup is page infrastructure, and it goes stale the same way content does. A short routine keeps it trustworthy:
- Write down the setup: the section file name, which templates use it, which metafield holds product FAQs and which app (if any) is turned off for FAQs.
- Recheck after theme updates and app installs. A new review or SEO app is the usual source of a second FAQPage block.
- Update answers when policies change. A shipping time or return window in an FAQ is a promise to the customer.
- Review questions quarterly against support tickets and site search, and replace questions nobody asks.
If you'd like a second pair of eyes on your structured data, duplicate markup and the rest of your technical SEO, talk to our team.
Book a free 30-minute call. We'll compare you with your top three competitors, show which pages are ready to move and give you a prioritized plan with the revenue math.