The product page
---import { getGeoffyProductMarkup } from "@geoffy/headless/astro";
const { lang, handle } = Astro.params;const geoffy = await getGeoffyProductMarkup( { siteKey: import.meta.env.GEOFFY_SITE_KEY }, handle, { canonicalUrl: `https://yourdomain.com/${lang}/products/${handle}`, locale: lang, },);---
<Layout> {geoffy && <Fragment set:html={geoffy.jsonLdScript} slot="head" />}
<YourProductUI handle={handle} />
{geoffy && <div data-geoffy-widget set:html={geoffy.widgetHtml} />}</Layout>The file path is an example; your route can be any shape, because you pass handle
explicitly.
What the helper returns
Section titled “What the helper returns”getGeoffyProductMarkup resolves to null, or to an object with two strings:
| Field | Contains | Where it goes |
|---|---|---|
jsonLdScript | a complete <script type="application/ld+json"> element | the <head>, via slot="head" |
widgetHtml | the widget markup, with its <style> inside it | wherever a shopper should see it |
Both are pre-rendered HTML strings, so both need set:html.
What handle must be
Section titled “What handle must be”| Your setup | handle is |
|---|---|
| Shopify behind your storefront | the Shopify product handle, the same value you already query the Storefront API with |
| No commerce platform | the segment your product URL pattern captured. Geoffy shows you the handle it recorded next to each crawled product |
See which kind of site is yours.
canonicalUrl is optional and worth passing
Section titled “canonicalUrl is optional and worth passing”Geoffy publishes against one canonical URL per product, and this helper runs wherever you call it. On a page that is not that URL it would otherwise return another page’s content.
Pass canonicalUrl and you get null back instead, so the {geoffy && …} guards you
already have render nothing.
If your site serves more than one language
Section titled “If your site serves more than one language”Geoffy has content for one locale of each product — the one its canonical URL names.
Calling the helper on every locale without canonicalUrl renders the canonical locale’s
widget, and a structured-data node in the canonical locale’s language, under a page
written in another one. Nothing errors; the page simply makes a claim in the wrong
language.
Pass locale and canonicalUrl, and you do not have to be careful about it. The helper
answers null on every locale except the published one:
const geoffy = await getGeoffyProductMarkup( { siteKey: import.meta.env.GEOFFY_SITE_KEY }, handle, { canonicalUrl: canonical, locale: lang }, // lang: the locale this page renders, e.g. "sr");On a page in another language the helper returns null without a request to Geoffy.
Only a clear language mismatch hides the content: if the helper cannot tell the language, it
returns the content.
If you want Geoffy on every locale today, the way to get it is one Geoffy site per locale — each with its own address, ownership check and catalogue. That works now, and it costs a separate crawl and publish for each. Per-locale content under a single site is not something this package can do yet. Tell us if you need it.
It never breaks your page
Section titled “It never breaks your page”The helper resolves to null when Geoffy has nothing published, when Geoffy is
unreachable or erroring, and when locale or canonicalUrl does not match. It never throws.
A product with nothing published is a normal state, not an error, and a page for it makes no request to Geoffy. So you can call the helper on every product page, published or not.
Your storefront is your business: if we are having a bad minute, the correct behaviour is for your product page to render exactly as it would have without us. The fetch carries its own timeout for the same reason — a build should not hang because our origin is wedged.