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.

Structured data never renders without the widget

Section titled “Structured data never renders without the widget”

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 the structured data without the widget 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.

The widget can render alone. When your site owns its structured data (the Structured data setting is Never, or the page already carries its own node of the same type), Geoffy sends none, and the component renders the widget with no script. That needs @geoffy/headless 0.3.4 or later; an older version renders nothing at all for such a page.

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.