Skip to content

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.

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 layoutReact hoists a script only with src and async={true}. Its docs: “Inline scripts are not de-duplicated or moved to the document <head>.”
generateMetadataEmits 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.

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.