Where to put the markup
Two decisions here, and only one of them changes whether a crawler reads you.
Where on the page the structured data sits does not. Google documents JSON-LD as valid
in the <head> and the <body>, and Geoffy’s own live check searches the whole
document rather than a region. Both placements pass, on both sides.
Whether it is server-rendered decides everything. Google can read JSON-LD injected by JavaScript — and that is the trap, because most of the AI crawlers this package exists for cannot. Treat Google as the exception rather than the rule: if the markup is not in the HTML your server sends, assume it is invisible.
Why Next.js renders it in the body
Section titled “Why Next.js renders it in the body”Because there is no supported way to do otherwise, and because Next recommends the body anyway. Both halves matter — the first means we could not offer head placement even if we wanted to, the second means you are not losing anything.
Route into <head> | Why it is not available |
|---|---|
An inline <script> in your page or layout | React hoists a script only with src and async={true}. Its docs: “Inline scripts are not de-duplicated or moved to the document <head>.” |
generateMetadata | Emits meta, link and title only. There is no script field |
next/script with strategy="beforeInteractive" | Does inject into the head — but “must be placed inside the root layout”, which never receives your product handle |
<script async src="…"> | React would hoist this one, and it would be worthless: async means the browser fetches it, so every crawler that does not run JavaScript sees an empty tag |
Next’s own guidance is “render structured data as a <script> tag in your layout.js or
page.js components”, and its example places it in the body. <GeoffyProduct> emits
exactly that shape, down to the < escaping their example uses.
The pair is not separable, and that is a feature
Section titled “The pair is not separable, and that is a feature”The widget and the structured data render together, wherever you place the component. That is the better trade on its own merits: you cannot ship one without the other by accident.
A page carrying structured data without the visible widget makes a claim to a crawler that
no shopper can see — so Geoffy holds such a product at code_not_added rather than
publishing it.
Where to put the widget
Section titled “Where to put the widget”Wherever a shopper should see it. Usually under your description or your buy box. Its position is entirely yours; Geoffy checks that it is present, never where.
Its root is a block-level <details> at width: 100%, so give it a full-width container
rather than a narrow column. Its CSS is scoped entirely under .geoffy-widget and
inherits your font and colour, so it cannot restyle the rest of your page and needs no
dark-mode handling from you.