Introduction
Scan a page of search results and you'll see some listings that come with a price, stock status, even a hierarchy like "Home > Category > Product." Meanwhile your own site shows a blue title and two lines of description. Same ranking position, very different pull.
That extra information isn't something search engines infer from the body text. It appears only because the page declares, in a machine-readable form, "what's written here is a price." With an off-the-shelf EC theme that declaration is produced automatically, and moving to headless stops it entirely.
This article covers what I declare on product pages, category pages, and the FAQ of an EC site selling motorcycle gear, plus the rules I settled on after running it for a while.
Structured Data Is a Second Copy of the Page, Invisible on Screen
The Same Content Held Two Ways: for People and for Machines
Structured data is a small block of information embedded in the HTML that never appears on screen. On a product page, the photo, name, and price are the part people see; the type, price, currency, stock, and hierarchy are the part machines read. Hold the same content in two formats — that's the whole idea.
Photo, product name, price, description
Type, price, currency, stock, hierarchy
Results carrying price, stock, and hierarchy
The point of the diagram: one page has two faces, one for people and one for machines.
The condition is that the two never contradict each other. Information declared only for machines that doesn't exist on screen, or a declared price that differs from the displayed one, gets the page treated as untrustworthy. You can't quietly inflate just one side.
Why I Chose JSON-LD
There are several ways to write structured data. One approach attaches attributes to individual HTML tags; I went with JSON-LD instead. It sits apart from the body HTML as a single block of declaration data on the page, so the visible markup stays untouched.
Scattered across HTML attributes. Breaks easily when the design changes
Written as one block separate from the body. Managed in a single place
In other words, you layer one sheet of declaration data on top of the body you built with SSR (server-side rendering, where the server assembles the HTML before returning it). It also means a design change is far less likely to break the declarations.
Three Kinds of Data on Product Pages
The Product Itself
This is the one with the biggest effect. The declared fields are the product name, description, brand, image, price, currency, stock status, product page URL, and product code.
With those fields in place, price and stock can start appearing in search results.
One thing I watch is what happens when a shopper switches color or size. In a headless build, variant switching (the same product in a different color or size) finishes instantly on screen. If the displayed price changes while the declared value stays stale, screen and declaration diverge. So the values used for display are passed straight through to the declaration.
Breadcrumbs
The reason a hierarchy like "Home > Riding Jackets > Product Name" shows in place of the URL is the breadcrumb declaration. It conveys the site structure and reads more clearly, which shows up in click-through rate.
The server determines which category the current page sits under
Arrange them as Home → Category → Product
Give each item its display name and page URL
Output on the server from the same source as the visible breadcrumb
Follow those four steps and the visible breadcrumb and the declared hierarchy are built from identical material.
The fiddly part was deciding where the names in the hierarchy come from. Categories often carry two names: a title written for search results and a short label used inside the site. The former tends to be long and stuffed with search terms, and using it directly makes the hierarchy hard to read. So the display name is decided in one place, and the visible breadcrumb, the declaration, and the link back to the category from a product page all use that same value.
Organization Information
The third is a block covering the operator's name, logo, official site URL, and social accounts. It matters less for any single product page than for how the site appears when someone searches the brand name, and for overall trust. Unglamorous, but you build it once and reuse it everywhere.
Declarations for Category Pages and the FAQ
Conveying Exactly the 24 Items on Display
Category listing pages get a declaration saying "this page collects products," along with a list of the products shown. The list carries display order, product name, product page URL, and a description excerpt.
The count is set to the 24 items shown when the page first opens. Adding products that aren't on screen would put the display and the declaration out of sync. It uses exactly the same material as the SSR output for the category page.
The Question-and-Answer Page
Pages of questions and answers declare those pairs directly. Every question on the page is collected, and the answers go in as plain strings with formatting tags removed.
Worth noting: the search result format that expands questions and answers inline is currently limited to certain kinds of sites, such as government and medical organizations. Declaring it on an EC site won't change how the results look right now. I still output it because conveying that the page consists of questions and answers has value on its own — and display formats change over time, so holding the data in the correct shape is a reasonable investment.
Three Rules I Settled On
Keep a Single Source for Every Value
Declared Values Must Match the Screen
If the price or stock in your declaration diverges from what the page actually displays, the page risks being treated as untrustworthy by search engines. Building both the screen and the declaration from the same SSR-fetched data makes that mismatch structurally impossible.
Calculate the display price and the declared price separately and they will diverge the moment a discount or member price enters the picture. Pass the displayed value straight through and that class of mismatch simply doesn't occur. It's also an easy rule to remember, so whoever touches the code next isn't left guessing.
Don't Declare Too Many Types
Adding data that doesn't correspond to what's on the page has no effect; it just produces more validation errors and more to maintain. For product pages, three types — product, breadcrumbs, and organization — were plenty. Keeping the list short also means fewer places to check when you review it later.
Always Validate After Publishing
Structured data renders perfectly on screen even if one character is wrong, so mistakes go unnoticed. I run product and category page URLs through the testing tool search engines publish, one at a time, to confirm the intended fields are being read. The parts built with help from an AI agent (AI-assisted development) are exactly the ones where I keep that manual verification step.
Conclusion
Structured data is the work of restating a page's content in a machine-readable format and attaching it. Product pages get the product, breadcrumbs, and organization; category pages get the list of products on display; question pages get the questions and answers. Hold to one rule — everything must match what's shown on screen — and none of it is difficult.
The work itself is unglamorous, but how your listings look in search changes visibly even at the same ranking. It's worth building early, alongside your SSR output.
SEO Design for Headless EC — SSR Visibility, Structured Data, Sitemaps
The big picture of SEO measures for headless EC.
Supplementing Crawler-Visible Content with SSR
The SSR content output that forms the foundation for structured data.
Generating and Caching Dynamic Sitemaps and robots.txt
Sitemap generation that follows products as they change, plus URL normalization.